├── .clang-format ├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── MigrationGuide.md ├── README.md ├── inc ├── avz.h ├── avz_asm.h ├── avz_card.h ├── avz_click.h ├── avz_cob_manager.h ├── avz_connector.h ├── avz_coroutine.h ├── avz_exception.h ├── avz_game_controllor.h ├── avz_global.h ├── avz_iterator.h ├── avz_logger.h ├── avz_memory.h ├── avz_painter.h ├── avz_pvz_struct.h ├── avz_replay.h ├── avz_script.h ├── avz_seh.h ├── avz_smart.h ├── avz_state_hook.h ├── avz_tick_runner.h ├── avz_time_queue.h ├── avz_timeline.h ├── avz_types.h ├── dsl │ ├── main.h │ ├── shorthand.h │ ├── shorthand_240205.h │ └── shorthand_240713.h ├── libavz.h └── minhook │ └── MinHook.h ├── metadata.json ├── release ├── env1 │ ├── 2020_07_10.zip │ ├── 2020_07_27.zip │ ├── 2020_08_10.zip │ ├── 2020_09_15.zip │ ├── 2020_11_01.zip │ ├── 2020_12_26.zip │ ├── 2021_02_11.zip │ ├── 2021_04_19.zip │ ├── 2021_08_20.zip │ ├── 2021_12_12.zip │ ├── 2022_02_13.zip │ ├── 2022_06_30.zip │ └── 2022_10_01.zip ├── env2 │ ├── 2.0.0_2022_12_08.zip │ ├── 2.1.0_2023_01_19.zip │ ├── 2.2.6_2023_04_05.zip │ ├── 2.3.3_2023_05_01.zip │ ├── 2.4.4_2023_06_15.zip │ ├── 2.5.1_2023_09_26.zip │ ├── 2.6.0_2024_01_13.zip │ ├── 2.7.0_2024_02_05.zip │ ├── 2.7.1_2024_05_10.zip │ ├── 2.8.0_2024_07_13.zip │ ├── 2.8.1_2024_08_16.zip │ ├── 2.8.2_2024_10_30.zip │ ├── 2.8.3_2025_02_25.zip │ ├── 2.8.4_2025_05_15.zip │ ├── 2.8.5_2025_07_11.zip │ ├── 2.8.6_2025_08_23.zip │ ├── 2022_02_13.zip │ ├── 2022_06_30.zip │ ├── 2022_10_01.zip │ └── 2022_11_24_2.0.0_preview.zip └── version.txt ├── src ├── avz_asm.cpp ├── avz_card.cpp ├── avz_click.cpp ├── avz_cob_manager.cpp ├── avz_connector.cpp ├── avz_coroutine.cpp ├── avz_game_controllor.cpp ├── avz_global.cpp ├── avz_hook.cpp ├── avz_logger.cpp ├── avz_memory.cpp ├── avz_painter.cpp ├── avz_replay.cpp ├── avz_script.cpp ├── avz_seh.cpp ├── avz_smart.cpp ├── avz_state_hook.cpp ├── avz_tick_runner.cpp ├── avz_time_queue.cpp ├── libavz.cpp └── minhook │ ├── buffer.cpp │ ├── buffer.h │ ├── hde32.cpp │ ├── hde32.h │ ├── hook.cpp │ ├── pstdint.h │ ├── table32.h │ ├── trampoline.cpp │ └── trampoline.h ├── tools └── injector │ ├── CMakeLists.txt │ └── src │ ├── injector.cpp │ ├── injector.h │ └── main.cpp └── tutorial ├── 01start.md ├── 02hello_avz.md ├── 03coding_style.md ├── 04reload_mode.md ├── 05card_shovel.md ├── 06co_await.md ├── 07connect_time.md ├── 08cob_manager_1.md ├── 08time.md ├── 09first_tas_script.md ├── 0catalogue.md ├── 10ice_filler.md ├── 11memory_func.md ├── 12second_tas_script.md ├── 13cob_manager_2.md ├── 14third_tas_script.md ├── 15cob_manager_3.md ├── 16cob_manager_4.md ├── 17cob_manager_5.md ├── 18connector.md ├── 19coroutine.md ├── 20game_controllor.md ├── 21read_memory_basic.md ├── 22tick_runner.md ├── 23painter.md ├── 24logger.md ├── 25state_hook.md ├── 26read_memory.md ├── 27replay.md ├── 28liang_yi.md ├── dsl ├── 0 前言.md ├── 1 时间轴是什么.md ├── 2 构建时间轴.md ├── 3 连接时间轴.md ├── 4 脚本示例:[FE] 经典四炮.md └── A 关于 shorthand.h.md ├── img ├── ImDisk.jpg ├── ImDisk1.jpg ├── ImDisk2.jpg ├── RE10.jpg ├── RE18.jpg ├── console.jpg ├── file.jpg ├── hello_avz.jpg ├── msgBox.jpg ├── painter.jpg ├── pvzGui.jpg └── timeline.png └── scripts ├── 4th_anniversary_celebration ├── alterit__fe_97_no_cob │ ├── alterit__fe_97_no_cob.cpp │ └── game1_14.dat ├── artificialabyss__re_10 │ ├── artificialabyss__re_10.cpp │ └── game1_15.dat ├── h2o__pe_3c8p │ ├── GetZombieAbscissa.h │ ├── PlantOperator.h │ ├── TickPlanterPlus.h │ └── h2o__pe_3c8p.cpp ├── pokey8__fe_10 │ ├── game1_13.dat │ ├── judge.h │ └── pokey8__fe_10.cpp ├── reisen__fe_24 │ ├── game1_14.dat │ └── reisen__fe_24.cpp └── shallow_dream__pe_alternative_16 │ ├── game1_13.dat │ └── shallow_dream__pe_alternative_16.cpp ├── dsl_jing_dian_4 ├── game1_14.dat └── jing_dian_4.cpp ├── jing_dian_12 ├── game1_13.dat ├── jing_dian_12_co_await.cpp └── jing_dian_12_connect.cpp ├── jing_dian_2 ├── game1_13.dat └── jing_dian_2.cpp ├── jing_dian_4 ├── game1_13.dat └── jing_dian_4.cpp ├── liang_yi ├── game1_13.dat ├── judge.h ├── liang_yi.cpp └── processor.h └── tian_tai_10 ├── game1_13.dat └── tian_tai_10.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: WebKit 2 | AlignTrailingComments: true 3 | Cpp11BracedListStyle: true 4 | 5 | BreakBeforeBraces: Attach 6 | #PackConstructorInitializers: NextLineOnly 7 | SpaceInEmptyBlock: false 8 | #RemoveBracesLLVM: true 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.cache/ 2 | /.vscode/ 3 | /bin/ 4 | /build/ 5 | /docs/ 6 | /MinGW/ 7 | /scripts/ 8 | /tutorial/draft/ 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(AsmVsZombies) 3 | 4 | # set global options 5 | add_compile_options(-m32 -std=c++20 -Wall) 6 | add_link_options(-m32) 7 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 8 | set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 9 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 10 | 11 | # build libavz 12 | aux_source_directory(src avz_srcs) 13 | aux_source_directory(src/minhook minhook_srcs) 14 | add_library(avz ${avz_srcs} ${minhook_srcs}) 15 | target_compile_options(avz PRIVATE -fexperimental-library) 16 | target_include_directories(avz PRIVATE inc) 17 | 18 | # build injector 19 | add_subdirectory(tools/injector) 20 | 21 | # 此条命令仅用于作者打包 avz,仅编译 avz 请注释这条命令 22 | add_custom_target(PackageAvz ALL 23 | COMMAND powershell -c "\ 24 | cd ${PROJECT_SOURCE_DIR}; \ 25 | rm bin/*.dll; \ 26 | Compress-Archive -Path bin/,inc/,src/,metadata.json -Force -DestinationPath release/env2/nightly.zip" 27 | DEPENDS avz injector 28 | ) 29 | -------------------------------------------------------------------------------- /MigrationGuide.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # 版本迁移文档 9 | 10 | ## 2.8.3 250225 11 | 12 | 1. `AAliveFilter` 不再包含已被蹦极抱走的植物和已生效的窝瓜;`AAliveFilter` 不再包含选卡界面的僵尸。 13 | 如果确实需要遍历这些对象,请使用 `ABasicFilter` 14 | 15 | ## 2.8.0 240713 16 | 17 | 1. `ATimeline` 的合并运算符改为 `&`,`ATimeline + ATimeline` 被弃用 18 | 2. `AAsm::RemoveZombie` 不再会产生死亡动画或掉落战利品;原本的函数改名为 `AAsm::KillZombie` 19 | 3. 在 DSL Shorthand 240713 中,窝瓜函数的名称改为 `W` 20 | 21 | ## 2.7.1 240510 22 | 23 | 1. 日志类内部实现更改为 `std::format`,因此 `{}` 在任何情况下都能作为格式化字符串使用,如果想显示出 `{}`,请使用 `{{}}` 24 | 25 | ## 2.3.3 230501 26 | 27 | 1. AWaitUntil 弃用,请使用 co_await 来代替此函数 28 | 29 | ## 2.0.0 221208 30 | 31 | 1. AStateHook 的四个虚函数的名字发生了变化 32 | 33 | ```C++ 34 | BeforeScript() -> _BeforeScript() 35 | AfterScript() -> _AfterScript() 36 | EnterFight() -> _EnterFight() 37 | ExitFight() -> _ExitFight() 38 | ``` 39 | 40 | ## 221124 2.0.0 preview 41 | 42 | 1. 这是一个重大更新,使用 221124 2.0.0 preview 需要重新下载新的环境开发包 43 | 44 | 2. 会带来 VSCode 插件的不兼容性,需要修改编译命令为 `-m32 -static -std=c++20 -g -Wall "__FILE_NAME__" -I "__AVZ_DIR__/inc" -lavz -lgdi32 -L "__AVZ_DIR__/bin" -shared -o "./bin/libavz.dll"`,注意粘贴复制的时候千万不要有换行符, 然后这个设置需要打开扩展栏,找到 AvZ 扩展,然后右键 AvZ 右下角的齿轮找到扩展设置, 将 `Avz Compiler Cmd` 修改为上面的内容。 45 | 46 | 3. 其他更新请看新的图文教程 47 | 48 | ## AvZ1 221001 49 | 50 | 1. 由于 SafePtr 已被弃用, 在脚本中使用 `SafePtr` 的用户直接使用原始指针 Type 即可, 51 | **同时函数 toUnsafe 被删除** 52 | 53 | ```C++ 54 | // 220630 55 | SafePtr zombie_array = GetMainObject()->zombieArray(); // 已弃用 56 | zombie_array.toUnsafe(); // 没问题 57 | 58 | // 221001 59 | Zombie* zombie_array = GetMainObject()->zombieArray(); 60 | zombie_array.toUnsafe(); // 错误 61 | ``` 62 | 63 | 注意 : 使用习惯为 `auto` 的用户可以无视 64 | 65 | 2. 由于一个设计失误, `Fliter` 的 `*` 运算符在之前的版本返回了错误的结果, 66 | 为了兼容性, 221001 版本依然支持之前的写法, 但是不推荐使用(已弃用). 67 | 68 | ```C++ 69 | // 220630 70 | // 已弃用 71 | AliveFilter zombie_filter; 72 | for (auto&& zombie : zombie_filter) { 73 | if (zombie->type() == HY_32) { 74 | // some code 75 | } 76 | } 77 | 78 | // 221001 79 | AliveFilter zombie_filter; 80 | for (auto&& zombie : zombie_filter) { 81 | if (zombie.type() == HY_32) { 82 | // some code 83 | } 84 | } 85 | ``` 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AsmVsZombies 2 | 3 | AvZ (Assembly vs. Zombies) 是一套使用 C++ 语言编写的高精度植物大战僵尸键控框架,理论由 yuchenxi0_0 提出,框架底层由 yuchenxi0_0 实现,和其他框架相似的接口由 vector-wlc 编写。 4 | 5 | AvZ 操作精度为理论上的绝对精准,使用这套框架将再也不用担心精度的问题,可在一定程度上减少录制视频次数,有效地完成视频制作。 6 | 7 | 本项目使用 [VSCode](https://code.visualstudio.com/) + LLVM-MinGW 进行代码编辑、编译和注入。 8 | 9 | ## 使用 10 | 11 | 请转到教程的目录:[GitLab](https://gitlab.com/vector-wlc/AsmVsZombies/blob/master/tutorial/0catalogue.md) / [GitHub](https://github.com/vector-wlc/AsmVsZombies/blob/master/tutorial/0catalogue.md) / [Gitee](https://gitee.com/vector-wlc/AsmVsZombies/blob/master/tutorial/0catalogue.md) 12 | 13 | ## 友情链接 14 | 15 | **注意:以下存储库的作者不是 AsmVsZombies 的作者,因此出现任何问题请咨询存储库中的相关作者。** 16 | 17 | AvZ 扩展功能库:[AvZLib](https://github.com/qrmd0/AvZLib) 18 | 19 | AvZ 脚本库:[AvZScript](https://github.com/qrmd0/AvZScript) 20 | 21 | ## 原理 22 | 23 | 在游戏主循环函数前面注入键控代码,使得键控脚本在每一帧都被调用,从而实现真正意义上 100 %精确的键控。 24 | 25 | ## 对比 26 | 27 | 从原理可以明显看出此套框架在理论实现上与传统框架截然不同,传统框架使用一个程序向 PvZ 窗口发送点击消息,而此套框架使用代码注入,直接入侵到程序内部,让游戏程序运行我们编写的脚本!其优缺点大致如下: 28 | 29 | > 缺点 30 | > 31 | > * 编写不慎可能会导致游戏崩溃 32 | > 33 | > 优点 34 | > 35 | > * 精度极高 36 | > * 脚本出现错误时提示更加人性化 37 | > * 对硬件配置 (CPU) 的要求低 38 | > * 对操作时间顺序不做严格要求 39 | 40 | ## 致谢 41 | 42 | * [yuchenxi2000/AssemblyVsZombies](https://github.com/yuchenxi2000/AssemblyVsZombies) 43 | * [lmintlcx/pvzscript](https://github.com/lmintlcx/pvzscripts) 44 | * [TsudaKageyu/minhook](https://github.com/TsudaKageyu/minhook) 45 | * [失控的指令(bilibili)](https://space.bilibili.com/147204150/) 46 | * [Power_tile(bilibili)](https://space.bilibili.com/367385512) 47 | * [六三enjoy(bilibili)](https://space.bilibili.com/660622963) 48 | * [alumkal(github)](https://github.com/alumkal) 49 | * 以及所有对此项目提出建议的使用者和开发人员 50 | -------------------------------------------------------------------------------- /inc/avz.h: -------------------------------------------------------------------------------- 1 | #ifndef __AVZ_H__ 2 | #define __AVZ_H__ 3 | 4 | #include "libavz.h" 5 | 6 | void AScript(); 7 | 8 | #define ACoScript() \ 9 | __ARealScript(); \ 10 | void AScript() { ACoLaunch(__ARealScript); } \ 11 | auto __ARealScript() -> decltype(__ARealScript()) 12 | 13 | #define ___AConCat(a, b) a##b 14 | #define __AConCat(a, b) ___AConCat(a, b) 15 | #define AAddStateHook(FuncName, ...) \ 16 | class : public A##FuncName##Hook { \ 17 | protected : \ 18 | virtual void _##FuncName() override __VA_ARGS__ \ 19 | } __AConCat(__aStateHook, __COUNTER__); 20 | 21 | #define AOnBeforeScript(...) AAddStateHook(BeforeScript, { __VA_ARGS__; }) 22 | #define AOnAfterScript(...) AAddStateHook(AfterScript, { __VA_ARGS__; }) 23 | #define AOnEnterFight(...) AAddStateHook(EnterFight, { __VA_ARGS__; }) 24 | #define AOnExitFight(...) AAddStateHook(ExitFight, { __VA_ARGS__; }) 25 | #define AOnAfterInject(...) AAddStateHook(AfterInject, { __VA_ARGS__; }) 26 | #define AOnBeforeTick(...) AAddStateHook(BeforeTick, { __VA_ARGS__; }) 27 | #define AOnAfterTick(...) AAddStateHook(AfterTick, { __VA_ARGS__; }) 28 | #define AOnBeforeExit(...) AAddStateHook(BeforeExit, { __VA_ARGS__; }) 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /inc/avz_card.h: -------------------------------------------------------------------------------- 1 | #ifndef __AVZ_CARD_H__ 2 | #define __AVZ_CARD_H__ 3 | 4 | #include "avz_state_hook.h" 5 | #include "avz_tick_runner.h" 6 | 7 | struct ACardName { 8 | APlantType plantType; 9 | int row; 10 | float col; 11 | }; 12 | 13 | class __ACardManager : public AOrderedBeforeScriptHook<-1>, 14 | public AOrderedEnterFightHook<-1> { 15 | public: 16 | void SelectCards(const std::vector& lst, int selectInterval); 17 | int GetCardIndex(APlantType plantType); 18 | std::vector Card(const std::vector& lst); 19 | APlant* Card(int seedIndex, int row, float col); 20 | APlant* Card(int seedIndex, const std::vector& lst); 21 | APlant* Card(APlantType plantType, int row, float col); 22 | APlant* Card(APlantType plantType, const std::vector& lst); 23 | const std::string& GetCardName(APlantType type); 24 | const std::string& GetCardName(ASeed* seed); 25 | 26 | protected: 27 | static std::vector _cardName; 28 | std::vector _selectCardVec; 29 | std::unordered_map _cardNameToIndexMap; 30 | int _selectInterval; 31 | ATickRunner _tickRunner; 32 | bool _isSelectCards = false; 33 | APlant* _CardWithoutCheck(int seedIndex, int row, float col); 34 | APlant* _BasicCard(int seedIndex, int row, float col); 35 | APlant* _BasicCard(int seedIndex, const std::vector& lst); 36 | bool _CheckCard(int seedIndex); 37 | void _ChooseSingleCard(); 38 | 39 | virtual void _BeforeScript() override; 40 | virtual void _EnterFight() override; 41 | }; 42 | 43 | inline __ACardManager __aCardManager; 44 | 45 | // 选择一堆卡片 46 | // ***注意:卡片名称与英文原版图鉴一致 47 | // *** 使用示例: 48 | // ASelectCards({ 49 | // AICE_SHROOM, // 寒冰菇 50 | // AM_ICE_SHROOM, // 模仿寒冰菇 51 | // ACOFFEE_BEAN, // 咖啡豆 52 | // ADOOM_SHROOM, // 毁灭菇 53 | // ALILY_PAD, // 荷叶 54 | // ASQUASH, // 倭瓜 55 | // ACHERRY_BOMB, // 樱桃炸弹 56 | // ABLOVER, // 三叶草 57 | // APUMPKIN, // 南瓜头 58 | // APUFF_SHROOM, // 小喷菇 59 | // }); 60 | // 61 | // ASelectCards({ 62 | // AICE_SHROOM, // 寒冰菇 63 | // AM_ICE_SHROOM, // 模仿寒冰菇 64 | // ACOFFEE_BEAN, // 咖啡豆 65 | // ADOOM_SHROOM, // 毁灭菇 66 | // ALILY_PAD, // 荷叶 67 | // ASQUASH, // 倭瓜 68 | // ACHERRY_BOMB, // 樱桃炸弹 69 | // ABLOVER, // 三叶草 70 | // APUMPKIN, // 南瓜头 71 | // APUFF_SHROOM, // 小喷菇 72 | // }, 1); --------------------------- 将选卡间隔更改为 1cs 以增加选卡速度 73 | // 74 | // ASelectCards({ 75 | // AICE_SHROOM, // 寒冰菇 76 | // AM_ICE_SHROOM, // 模仿寒冰菇 77 | // ACOFFEE_BEAN, // 咖啡豆 78 | // ADOOM_SHROOM, // 毁灭菇 79 | // ALILY_PAD, // 荷叶 80 | // ASQUASH, // 倭瓜 81 | // ACHERRY_BOMB, // 樱桃炸弹 82 | // ABLOVER, // 三叶草 83 | // APUMPKIN, // 南瓜头 84 | // APUFF_SHROOM, // 小喷菇 85 | // }, 0); --------------------------- 将选卡间隔更改为 0cs 以进行极速选卡,注意 0 为特殊值,效果为瞬间跳到战斗界面 86 | // 87 | // ASelectCards({ 88 | // AICE_SHROOM, // 寒冰菇 89 | // }, 0); --------------------------- 将选卡间隔更改为 0cs 以进行极速选卡,注意 0 为特殊值,效果为瞬间跳到战斗界面,然后剩下的九个卡槽会被随机填充 90 | inline void ASelectCards(const std::vector& lst = {}, int selectInterval = 17) { 91 | __aCardManager.SelectCards(lst, selectInterval); 92 | } 93 | 94 | inline void ASelectCards(std::initializer_list lst, int selectInterval = 17) { 95 | __aCardManager.SelectCards(lst, selectInterval); 96 | } 97 | 98 | // 根据卡片名称得到卡片索引 99 | // *** 注意:卡片索引从 0 开始, 不存在返回 -1 100 | // *** 使用示例: 101 | // AGetCardIndex(AICE_SHROOM) ---- 得到寒冰菇的卡片索引 102 | __ANodiscard inline int AGetCardIndex(APlantType plantType) { 103 | return __aCardManager.GetCardIndex(plantType); 104 | } 105 | 106 | // 根据卡片名称得到卡片指针 107 | // // *** 注意:不存在返回 nullptr 108 | // *** 使用示例: 109 | // AGetCardIndex(AICE_SHROOM) ---- 得到寒冰菇的卡片指针 110 | __ANodiscard ASeed* AGetCardPtr(APlantType plantType); 111 | 112 | // 用卡函数 113 | // *** 注意:ACard 不支持根据卡槽位置用卡的多张调用形式 114 | // ACard 一次种一张卡片会返回一个 APlant*, 如果值为 nullptr,则 ACard 种植失败,否则代表 ACard 刚种下植物的对象指针 115 | // 一次种植多张卡片会返回 std::vector, 意义与上述相同 116 | // *** 使用示例: 117 | // ACard(1, 2, 3)---------选取第1张卡片,放在2行,3列 118 | // ACard(1, {{2, 3}, {3, 4}})--------选取第1张卡片,优先放在2行,3列,其次放在3行,4列 119 | // 以下用卡片名称使用 Card,卡片名称为拼音首字母,具体参考图鉴 120 | // ACard({{CHERRY_BOMB, 2, 3}, {JALAPENO, 3, 4}})---------选取樱桃卡片,放在2行,3列,选取辣椒卡片,放在3行,4列 121 | // ACard({AHY_16, AHMG_15, AKFD_35, ANGT_30}, 2, 3) ----- 将荷叶,毁灭菇,咖啡豆,南瓜头放在二行三列 122 | // ACard({AHY_16, AHMG_15, AKFD_35, ANGT_30}, {{3, 2}, {3, 3}, {3, 4}}) ---- 将荷叶,毁灭菇,咖啡豆,南瓜头放在二行三列 三行三列 三行四列 123 | // 注意是先将第一颗植物尝试所有位置,在进行下一个植物的尝试 124 | std::vector ACard(const std::vector& lst); 125 | APlant* ACard(int seedIndex, int row, float col); 126 | APlant* ACard(int seedIndex, const std::vector& lst); 127 | APlant* ACard(int seedIndex, const std::vector& lst); 128 | APlant* ACard(int seedIndex, std::initializer_list lst); 129 | APlant* ACard(APlantType plantType, int row, float col); 130 | APlant* ACard(APlantType plantType, const std::vector& lst); 131 | APlant* ACard(APlantType plantType, const std::vector& lst); 132 | APlant* ACard(APlantType plantType, std::initializer_list lst); 133 | std::vector ACard(const std::vector& plantTypeVec, int row, float col); 134 | std::vector ACard(const std::vector& plantTypeVec, const std::vector& lst); 135 | std::vector ACard(const std::vector& plantTypeVec, const std::vector& lst); 136 | std::vector ACard(const std::vector& plantTypeVec, std::initializer_list lst); 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /inc/avz_click.h: -------------------------------------------------------------------------------- 1 | #ifndef __AVZ_CLICK_H__ 2 | #define __AVZ_CLICK_H__ 3 | 4 | #include "avz_logger.h" 5 | 6 | struct AShovelPosition { 7 | int row; 8 | float col; 9 | int targetType; 10 | 11 | AShovelPosition(int row_, float col_, int targetType_ = -1) 12 | : row(row_) 13 | , col(col_) 14 | , targetType(targetType_) {} 15 | 16 | AShovelPosition(int row_, float col_, bool pumpkin) 17 | : row(row_) 18 | , col(col_) 19 | , targetType(pumpkin ? -2 : -1) {} 20 | }; 21 | 22 | // 将格子转换成坐标 23 | void AGridToCoordinate(int row, float col, int& x, int& y); 24 | std::pair AGridToCoordinate(int row, float col); 25 | 26 | // 点击格子 27 | // *** 使用示例: 28 | // AClickGrid(3, 4)---- 点击格子(3, 4) 29 | // AClickGrid(3, 4, 10)---- 向下偏移10像素点击格子(3, 4) 30 | void AClickGrid(int row, float col, int offset = 0); 31 | 32 | // 点击种子/卡片 33 | // *** 使用示例: 34 | // AClickSeed(1) ----- 点击第一个种子 35 | void AClickSeed(int seedIndex); 36 | 37 | // 鼠标左击 38 | // ALeftClick(400, 300)-----点击 PVZ 窗口中央 39 | void ALeftClick(int x, int y); 40 | 41 | // 铲除植物函数 42 | // *** 使用示例: 43 | // AShovel(4, 6)--------铲除4行6列的植物,如果植物有南瓜保护默认铲除被保护植物 44 | // AShovel(4, 6, APUMPKIN)---铲除4行6列的植物,如果植物有南瓜保护铲除南瓜 45 | // AShovel({{3, 6},{4, 6}})------铲除3行6列,4行6列的植物 46 | void AShovel(int row, float col, bool pumpkin); 47 | void AShovel(int row, float col, int targetType = -1); 48 | void AShovel(const std::vector& lst); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /inc/avz_connector.h: -------------------------------------------------------------------------------- 1 | #ifndef __AVZ_CONNECTOR_H__ 2 | #define __AVZ_CONNECTOR_H__ 3 | 4 | #include "avz_coroutine.h" 5 | #include "avz_tick_runner.h" 6 | 7 | class ATimeConnectHandle { 8 | public: 9 | using TimeIter = std::optional<__ATimeIter>; 10 | 11 | ATimeConnectHandle() = default; 12 | ATimeConnectHandle(TimeIter iter, const ATime& time) 13 | : _iter(iter), _time(time) {} 14 | 15 | void Stop(); 16 | 17 | operator bool() { 18 | return _iter.has_value(); 19 | } 20 | 21 | protected: 22 | TimeIter _iter; 23 | ATime _time; 24 | }; 25 | 26 | // 创建一条连接, 连接创建成功之后会返回一个类型为 AConnectHandle 的对象作为连接控制器 27 | // *** 特别注意:如果连接创建失败, 连接控制器将被赋值为 nullptr. 28 | // AConnect 最后一个参数为运行方式 29 | // 运行方式为 true 时, AConnect 创建的连接选卡界面和高级暂停时都生效, 反之不生效 30 | // *** 使用示例: 31 | // ALogger logger; 32 | // AConnectHandle keyHandle; 33 | // 34 | // void AScript() 35 | // { 36 | // logger.SetLevel({ALogLevel::INFO}); 37 | // 38 | // // 在时间点 (1, -95) 发两门炮 39 | // auto timeHandle = AConnect(ATime(1, -95), [=] { 40 | // aCobManager.Fire({{2, 9}, {5, 9}}); 41 | // }); 42 | // timeHandle.Stop(); // 让上面这个连接失效 43 | // 44 | // 45 | // // 在当前时间点 100cs 之后开始不断尝试种植小喷菇, 直到小喷菇种植成功 46 | // // 这里的 ANowDelayTime 会返回一个 ATime 对象 47 | // AConnect(ANowDelayTime(100), [] { 48 | // if (AIsSeedUsable(AXPG_8)) { 49 | // ACard(AXPG_8, 1, 1); 50 | // return false; 51 | // } 52 | // return true; 53 | // }); 54 | // 55 | // 56 | // // 按下 0 键弹出一个窗口, 显示 hello, 注意 0 是单引号 57 | // keyHandle = AConnect('0', [] { logger.Info("hello"); }); 58 | // 59 | // // 按下 E 键控制 0 键的是否暂停 60 | // // 如果 0 键此时暂停生效, 按下 E 键便会继续生效, 反之相反 61 | // AConnect('E', [] { 62 | // if (keyHandle.isPaused()) { 63 | // keyHandle.GoOn(); 64 | // } else { 65 | // keyHandle.Pause(); 66 | // } 67 | // }); 68 | // 69 | // // 按下 Q 键控制 0 键行为, 即将 0 键的显示变为 world 70 | // AConnect('Q', [] { 71 | // keyHandle.Stop(); // 注意此时 keyHandle 已失效 72 | // keyHandle = AConnect('0', [] { logger.Info("world"); }); // 此时 keyHandle 重新有效 73 | // }); 74 | // 75 | // // AConnect 第一个参数还可传入一个 bool Functor(), 如果此函数返回 true, 则会执行后面的操作 76 | // // 这个示例就是游戏每 10 秒钟显示一个 world 的窗口 77 | // AConnect([] { return AGetMainObject()->GameClock() % 1000 == 0; }, [] { logger.Info("world"); }); 78 | // 79 | // } 80 | template 81 | requires __AIsOperation 82 | ATimeConnectHandle AConnect(const ATime& time, Op&& op) { 83 | auto timeIter = __aOpQueueManager.Push(time, __ABoolOperation(std::forward(op))); 84 | return ATimeConnectHandle(timeIter, time); 85 | } 86 | 87 | template 88 | requires __AIsCoroutineOp 89 | ATimeConnectHandle AConnect(const ATime& time, Op&& op) { 90 | return AConnect(time, ACoFunctor(std::forward(op))); 91 | } 92 | 93 | template 94 | requires __AIsPredicate 95 | ATimeConnectHandle AConnect(const ATime& time, Sess&& func) { 96 | return AConnect(time, [func = std::forward(func)]() mutable { 97 | auto tickRunner = std::make_shared(); 98 | tickRunner->Start([func = std::move(func), tickRunner] { 99 | if(!func()) 100 | tickRunner->Stop(); }, false); 101 | }); 102 | } 103 | 104 | using AConnectHandle = ATickHandle; 105 | 106 | template 107 | requires __AIsPredicate
 && __AIsOperation
108 | AConnectHandle AConnect(Pre&& pre, Op&& op, int runMode = ATickRunner::ONLY_FIGHT, int priority = 0) {
109 |     auto func = [pre = std::forward
(pre), op = std::forward(op)]() mutable {
110 |         if (pre())
111 |             op();
112 |     };
113 |     auto&& ret = __aig.tickManagers[runMode].Insert(std::move(func), priority);
114 |     return AConnectHandle(ret.idx, ret.id, runMode, priority);
115 | }
116 | 
117 | template 
118 |     requires __AIsPredicate
 && __AIsCoroutineOp
119 | AConnectHandle AConnect(Pre&& pre, Op&& op, int runMode = ATickRunner::ONLY_FIGHT, int priority = 0) {
120 |     return AConnect(std::forward
(pre), ACoFunctor(std::forward(op)), runMode, priority);
121 | }
122 | 
123 | class __AKeyManager : public AOrderedExitFightHook<-1> {
124 | public:
125 |     enum KeyState {
126 |         VALID,
127 |         UNKNOWN,
128 |         REPEAT,
129 |     };
130 |     __AKeyManager();
131 |     static KeyState ToValidKey(AKey& key);
132 |     static void AddKey(AKey key, AConnectHandle connectHandle) {
133 |         _keyMap.emplace(key, connectHandle);
134 |     }
135 |     static const std::string& ToName(AKey key) {
136 |         return _keyVec[key];
137 |     }
138 | 
139 | protected:
140 |     static std::vector _keyVec;
141 |     static std::unordered_map _keyMap;
142 |     virtual void _ExitFight() override {
143 |         _keyMap.clear();
144 |     }
145 | };
146 | 
147 | inline __AKeyManager __akm; // AStateHook
148 | 
149 | template 
150 |     requires __AIsCoOpOrOp
151 | AConnectHandle AConnect(AKey key, Op&& op, int priority = 0, int runMode = ATickRunner::GLOBAL) {
152 |     if (__AKeyManager::ToValidKey(key) != __AKeyManager::VALID)
153 |         return AConnectHandle();
154 |     auto wasPressed = std::make_shared(false);
155 |     auto keyFunc = [key, wasPressed]() -> bool {
156 |         bool isPressed = (GetAsyncKeyState(key) & 0x8000) == 0x8000;
157 |         bool activate = isPressed && !*wasPressed;
158 |         *wasPressed = isPressed;
159 |         auto pvzHandle = AGetPvzBase()->MRef(0x350);
160 |         return (activate && GetForegroundWindow() == pvzHandle);              // 检测 pvz 是否为顶层窗口
161 |     };
162 |     auto handle = AConnect(std::move(keyFunc), std::forward(op), runMode, priority);
163 |     __AKeyManager::AddKey(key, handle);
164 |     return handle;
165 | }
166 | 
167 | #endif
168 | 


--------------------------------------------------------------------------------
/inc/avz_coroutine.h:
--------------------------------------------------------------------------------
  1 | #ifndef __AVZ_COROUTINE_H__
  2 | #define __AVZ_COROUTINE_H__
  3 | 
  4 | #include "avz_state_hook.h"
  5 | #include "avz_time_queue.h"
  6 | #include 
  7 | #include 
  8 | 
  9 | class __ACoHandleManager : public AOrderedExitFightHook<-10> {
 10 | public:
 11 |     static void Add(std::coroutine_handle<> handle) {
 12 |         _handleSet.emplace(handle.address());
 13 |     }
 14 | 
 15 |     static void Remove(std::coroutine_handle<> handle) {
 16 |         _handleSet.erase(handle.address());
 17 |     }
 18 | 
 19 | protected:
 20 |     static std::unordered_set _handleSet;
 21 |     virtual void _ExitFight() override;
 22 | };
 23 | 
 24 | class __AWait {
 25 | public:
 26 |     __AWait() = default;
 27 |     __AWait(const ATime& time)
 28 |         : _time(time) {}
 29 | 
 30 |     template 
 31 |         requires __AIsPredicate
 32 |     __AWait(Func&& func)
 33 |         : _time(ATime(-1, -1))
 34 |         , _predication(std::forward(func)) {}
 35 | 
 36 |     bool await_ready() const;
 37 |     void await_resume();
 38 |     void await_suspend(std::coroutine_handle<> handle);
 39 | 
 40 | private:
 41 |     ATime _time;
 42 |     APredication _predication;
 43 | };
 44 | 
 45 | #define __ACoNodiscard [[nodiscard("\n裸启动协程会导致内存访问错误问题, 请使用以下方式安全启动协程 " \
 46 |                                    "\n1. 立即启动 : ACoLaunch(协程函数名);"                                              \
 47 |                                    "\n2. 连接启动 : AConnect(时间/条件, 协程函数名)")]]
 48 | 
 49 | struct __ACoNodiscard ACoroutine {
 50 | 
 51 | #undef __ACoNodiscard
 52 |     struct promise_type {
 53 |         std::shared_ptr> ptr;
 54 |         __AWait await_transform(int delayTime) {
 55 |             return ANowDelayTime(delayTime);
 56 |         }
 57 |         __AWait await_transform(const ATime& time) {
 58 |             return time;
 59 |         }
 60 |         template 
 61 |             requires __AIsPredicate
 62 |         __AWait await_transform(Func&& func) {
 63 |             return std::forward(func);
 64 |         }
 65 |         auto get_return_object() {
 66 |             return ACoroutine {std::coroutine_handle::from_promise(*this)};
 67 |         }
 68 |         auto initial_suspend() {
 69 |             return std::suspend_always {};
 70 |         }
 71 |         auto final_suspend() noexcept {
 72 |             return std::suspend_never {};
 73 |         }
 74 |         void unhandled_exception() {}
 75 |         void return_void() {}
 76 |         ~promise_type() {
 77 |             aLogger->Info("协程退出");
 78 |         }
 79 |     };
 80 | 
 81 |     ACoroutine(std::coroutine_handle handle)
 82 |         : _handle(handle) {
 83 |     }
 84 | 
 85 |     void SetPtr(std::shared_ptr>& ptr) {
 86 |         _handle.promise().ptr = ptr;
 87 |         _handle.resume();
 88 |     }
 89 | 
 90 | protected:
 91 |     std::coroutine_handle _handle;
 92 | };
 93 | 
 94 | inline __ACoHandleManager __aCoHandleManager;
 95 | 
 96 | using ACoroutineOp = std::function;
 97 | 
 98 | template 
 99 | concept __AIsCoroutineOp = std::is_invocable_r_v;
100 | 
101 | // 判断此类型是协程函数或者普通函数
102 | template 
103 | concept __AIsCoOpOrOp = __AIsCoroutineOp || __AIsOperation;
104 | 
105 | class ACoFunctor {
106 | public:
107 |     template 
108 |         requires __AIsCoroutineOp
109 |     ACoFunctor(Op&& op) {
110 |         _functor = std::make_shared(std::forward(op));
111 |     }
112 | 
113 |     ACoFunctor(ACoFunctor&& rhs)
114 |         : _functor(std::move(rhs._functor)) {
115 |     }
116 | 
117 |     ACoFunctor(const ACoFunctor& rhs)
118 |         : _functor(rhs._functor) {
119 |     }
120 | 
121 |     ACoFunctor& operator=(ACoFunctor&& rhs) {
122 |         this->_functor = std::move(rhs._functor);
123 |         return *this;
124 |     }
125 | 
126 |     ACoFunctor& operator=(const ACoFunctor& rhs) {
127 |         this->_functor = rhs._functor;
128 |         return *this;
129 |     }
130 | 
131 |     void operator()();
132 | 
133 | protected:
134 |     std::shared_ptr _functor = nullptr;
135 | };
136 | 
137 | #define ACoLaunch(...) ACoFunctor {__VA_ARGS__}()
138 | 
139 | #endif
140 | 


--------------------------------------------------------------------------------
/inc/avz_exception.h:
--------------------------------------------------------------------------------
 1 | #ifndef __AVZ_EXCEPTION_H__
 2 | #define __AVZ_EXCEPTION_H__
 3 | 
 4 | #include "avz_types.h"
 5 | #include 
 6 | #include 
 7 | 
 8 | class AException : public std::exception {
 9 | public:
10 |     explicit AException(const char* message)
11 |         : msg_(message) {}
12 | 
13 |     explicit AException(const std::string& message)
14 |         : msg_(message) {}
15 | 
16 |     virtual ~AException() noexcept {}
17 | 
18 |     __ANodiscard virtual const char* what() const noexcept {
19 |         return msg_.c_str();
20 |     }
21 | 
22 | protected:
23 |     std::string msg_;
24 | };
25 | 
26 | // 通过抛出异常的形式让脚本立即停止运行
27 | inline void ATerminate(const std::string& reason = "unknown") {
28 |     throw AException("终止脚本运行,附加信息 : " + reason);
29 | }
30 | 
31 | constexpr const char* ASTR_GAME_RET_MAIN_UI = "game return main ui";
32 | 
33 | // 通过抛出异常的形式通知 AvZ 游戏退出了战斗界面
34 | inline void AExitFight() {
35 |     throw AException(ASTR_GAME_RET_MAIN_UI);
36 | }
37 | 
38 | #endif
39 | 


--------------------------------------------------------------------------------
/inc/avz_game_controllor.h:
--------------------------------------------------------------------------------
  1 | #ifndef __AVZ_GAME_CONTROLLOR_H__
  2 | #define __AVZ_GAME_CONTROLLOR_H__
  3 | 
  4 | #include "avz_logger.h"
  5 | #include "avz_memory.h"
  6 | 
  7 | class __AGameControllor : public AOrderedExitFightHook<-1> {
  8 | public:
  9 |     __AGameControllor();
 10 |     APredication isSkipTick = []() -> bool {
 11 |         return false;
 12 |     };
 13 | 
 14 |     template 
 15 |         requires __AIsPredicate
 && __AIsOperation
 16 |     void SkipTick(Pre&& pre, CallBack&& callback) {
 17 |         if (!_CheckSkipTick())
 18 |             return;
 19 |         isSkipTick = [pre = std::forward
(pre), //
 20 |                          callback = std::forward(callback), this]() mutable {
 21 |             auto gameUi = AGetPvzBase()->GameUi();
 22 |             if (gameUi == 3 && pre())
 23 |                 return true;
 24 |             isSkipTick = []() -> bool { return false; };
 25 |             if (gameUi == 3)
 26 |                 callback();
 27 |             return false;
 28 |         };
 29 |     }
 30 |     template 
 31 |         requires __AIsPredicate
 32 |     void SkipTick(Pre&& pre) {
 33 |         SkipTick(std::forward
(pre), [] {});
 34 |     }
 35 |     void SkipTick(int wave, int time);
 36 | 
 37 |     void SetAdvancedPause(bool isAdvancedPaused, bool isPlaySound = true, DWORD rectColor = AArgb(0x7f, 0, 0, 0));
 38 | 
 39 |     void SetUpdateWindow(bool isUpdateWindow);
 40 | 
 41 |     // 用于在高级暂停下的游戏刷新处理
 42 |     // 让高级暂停更加丝滑
 43 |     void UpdateAdvancedPause();
 44 | 
 45 |     bool isAdvancedPaused = false;
 46 | 
 47 |     bool isUpdateWindow = true;
 48 | 
 49 |     // 确保能否刷新游戏主要对象
 50 |     class UpdateGameObjGuard {
 51 |     public:
 52 |         UpdateGameObjGuard() {
 53 |             _asmBackup = AMRef(_UPDATE_ASM_ADDR_BEGIN);
 54 |             AMRef(_UPDATE_ASM_ADDR_BEGIN) = _oriAsm;
 55 |         }
 56 |         ~UpdateGameObjGuard() {
 57 |             AMRef(_UPDATE_ASM_ADDR_BEGIN) = _asmBackup;
 58 |         }
 59 | 
 60 |     private:
 61 |         uint16_t _asmBackup = 0;
 62 |     };
 63 | 
 64 | protected:
 65 |     virtual void _ExitFight() override;
 66 | 
 67 |     bool _CheckSkipTick();
 68 | 
 69 |     // pvz 每帧的更新汇编代码起始地址
 70 |     static constexpr uintptr_t _UPDATE_ASM_ADDR_BEGIN = 0x41600E;
 71 | 
 72 |     // 跳过游戏更新的汇编代码
 73 |     // jmp 2a
 74 |     static constexpr uint16_t _JMP_ASM = 0x2AEB;
 75 | 
 76 |     // 保存原本的机器码
 77 |     static uint16_t _oriAsm;
 78 | 
 79 |     int _pvzHeight = 0;
 80 |     int _pvzWidth = 0;
 81 | 
 82 |     DWORD _rectColor = AArgb(0x7f, 0, 0, 0);
 83 | };
 84 | 
 85 | inline __AGameControllor __aGameControllor;
 86 | 
 87 | // 跳到游戏指定时刻
 88 | // *** 注意使用此函数时不能使用高级暂停
 89 | // *** 使用示例
 90 | // ASkipTick(1, 200) ------ 跳到时刻点 (1, 200)
 91 | //
 92 | // 跳到指定条件为 false 的游戏帧
 93 | // *** 注意使用此函数时不能使用高级暂停
 94 | // *** 使用示例 : 直接跳到位置为 {1, 3}, {1, 5} 春哥死亡时的游戏帧
 95 | // auto condition = [=]() {
 96 | //     std::vector results;
 97 | //     GetPlantIndices({{1, 3}, {1, 5}}, YMJNP_47, results);
 98 | //
 99 | //     for (auto result : results) {
100 | //         if (result < 0) {
101 | //             return false;
102 | //         }
103 | //     }
104 | //     return true;
105 | // };
106 | //
107 | // auto callback = [=]() {
108 | //     // 写春哥没了的提示代码,比如用一个 ALogger 显示信息
109 | // };
110 | //
111 | // ASkipTick(condition, callback);
112 | template 
113 | void ASkipTick(Args&&... args) {
114 |     __aGameControllor.SkipTick(std::forward(args)...);
115 | }
116 | 
117 | // 设定高级暂停
118 | // *** 注意开启高级暂停时不能使用跳帧
119 | // *** 特别注意的是 `ASetAdvancedPause` 一旦使得程序进入高级暂停状态后,
120 | //     除了模式为 GLOBAL 和 AFTER_INJECT 的帧运行以及 AfterTick BeforeTick 的状态钩,框架将不再执行其他代码,
121 | // *** 使用示例
122 | // ASetAdvancedPause(true) ------ 开启高级暂停
123 | // ASetAdvancedPause(false) ------ 关闭高级暂停
124 | // ASetAdvancedPause(true, true) ------ 开启高级暂停,并播放相关音效
125 | // ASetAdvancedPause(true, AArgb(0x7f, 0, 0, 0)) ------ 开启高级暂停,并在暂停时在 pvz 顶层绘制颜色为 AArgb(0x7f, 0, 0, 0) 的全屏矩形
126 | inline void ASetAdvancedPause(bool isAdvancedPaused, bool isPlaySound = true, DWORD rectColor = AArgb(0x7f, 0, 0, 0)) {
127 |     __aGameControllor.SetAdvancedPause(isAdvancedPaused, isPlaySound, rectColor);
128 | }
129 | 
130 | // 设定游戏窗口是否更新
131 | // *** 注意此函数与高级暂停的区别是:高级暂停开启时鼠标和种植物的渲染动画还是在的,
132 | //     但是此函数设置为 false 时,游戏都不会渲染
133 | // *** 使用示例
134 | // ASetUpdateWindow(true) ------ 游戏更新窗口
135 | // ASetUpdateWindow(false) ------ 游戏不更新窗口
136 | inline void ASetUpdateWindow(bool isUpdateWindow) {
137 |     __aGameControllor.SetUpdateWindow(isUpdateWindow);
138 | }
139 | 
140 | #endif
141 | 


--------------------------------------------------------------------------------
/inc/avz_logger.h:
--------------------------------------------------------------------------------
  1 | #ifndef __AVZ_LOGGER_H__
  2 | #define __AVZ_LOGGER_H__
  3 | 
  4 | #include "avz_painter.h"
  5 | #include 
  6 | #include 
  7 | #include 
  8 | #include 
  9 | #include 
 10 | 
 11 | enum class ALogLevel {
 12 |     INFO,
 13 |     DEBUG,
 14 |     WARNING,
 15 |     ERROR,
 16 | };
 17 | 
 18 | class AAbstractLogger : public AOrderedBeforeScriptHook<-32767> {
 19 | public:
 20 |     __ANodiscard const std::string& GetPattern() const {
 21 |         return _pattern;
 22 |     }
 23 | 
 24 |     void SetPattern(std::string_view pattern) {
 25 |         _pattern = pattern;
 26 |     }
 27 | 
 28 |     void SetHeaderStyle(std::string_view headerStyle) {
 29 |         _headerStyle = headerStyle;
 30 |     }
 31 | 
 32 |     void SetLevel(const std::vector& levelVec) {
 33 |         _level = 0;
 34 |         for (auto&& level : levelVec)
 35 |             _level |= (1 << int(level));
 36 |     }
 37 | 
 38 |     void SetSuffix(const std::string& suffix) { _suffix = suffix; }
 39 | 
 40 |     template 
 41 |     void Info(FormatStr&& formatStr, Args&&... args) {
 42 |         _Format(ALogLevel::INFO, std::forward(formatStr), std::forward(args)...);
 43 |     }
 44 | 
 45 |     template 
 46 |     void Debug(FormatStr&& formatStr, Args&&... args) {
 47 |         _Format(ALogLevel::DEBUG, std::forward(formatStr), std::forward(args)...);
 48 |     }
 49 | 
 50 |     template 
 51 |     void Warning(FormatStr&& formatStr, Args&&... args) {
 52 |         _Format(ALogLevel::WARNING, std::forward(formatStr), std::forward(args)...);
 53 |     }
 54 | 
 55 |     template 
 56 |     void Error(FormatStr&& formatStr, Args&&... args) {
 57 |         _Format(ALogLevel::ERROR, std::forward(formatStr), std::forward(args)...);
 58 |     }
 59 | 
 60 | protected:
 61 |     std::string _pattern;
 62 |     std::string _headerStyle;
 63 |     uint8_t _level = 0b1111; // 每一个 bit 代表这个水平的日志开不开(1是开)
 64 |     std::string _bufStr;
 65 |     std::string _suffix = "\n";
 66 |     const std::vector _levelStr = {
 67 |         "INFO",
 68 |         "DEBUG",
 69 |         "WARNING",
 70 |         "ERROR",
 71 |     };
 72 |     virtual void _BeforeScript() override;
 73 |     virtual void _Output(ALogLevel level, std::string&& str) = 0;
 74 | 
 75 |     static void _Replace(std::string& content, std::string_view pattern, std::string_view replaceStr);
 76 | 
 77 |     std::string _CreateHeader(ALogLevel level);
 78 | 
 79 |     void _Format(ALogLevel level, std::string formatStr, auto&&... args) {
 80 |         if (((1 << int(level)) & _level) == 0)
 81 |             return;
 82 |         auto header = _CreateHeader(level);
 83 |         _Replace(formatStr, _pattern, "{}");
 84 |         std::string message;
 85 |         try {
 86 |             message = std::vformat(formatStr + _suffix, std::make_format_args(std::forward(args)...));
 87 |         } catch (const std::format_error& e) {
 88 |             message = "格式化错误: " + std::string(e.what());
 89 |         }
 90 |         _Output(level, header + message);
 91 |     }
 92 | };
 93 | 
 94 | struct AFile {};
 95 | struct AConsole {};
 96 | struct APvzGui {};
 97 | struct AMsgBox {
 98 |     static void Show(const std::string& str) {
 99 |         MessageBoxW(nullptr, AStrToWstr(str).c_str(), L"AMsgBox", MB_OK);
100 |     }
101 | };
102 | 
103 | template 
104 | class ALogger;
105 | 
106 | template <>
107 | class ALogger : public AAbstractLogger, public AOrderedExitFightHook<32767> {
108 | public:
109 |     ALogger(const std::string& fileName)
110 |         : _fileName(fileName) {}
111 | 
112 |     // 清除文件中的所有内容
113 |     // return true: 清除成功
114 |     // return false: 清除失败
115 |     bool Clear();
116 | 
117 | protected:
118 |     virtual void _BeforeScript() override;
119 |     virtual void _ExitFight() override;
120 |     std::string _fileName;
121 |     std::wofstream _outFile;
122 |     virtual void _Output(ALogLevel level, std::string&& str) override;
123 | };
124 | 
125 | template <>
126 | class ALogger : public AAbstractLogger {
127 | public:
128 |     ALogger();
129 |     ~ALogger();
130 |     // 设置显示颜色
131 |     // *** 使用示例:
132 |     // SetColor(ALogLevel::INFO, FOREGROUND_BLUE) ------ 将 INFO 等级的显示颜色设置为蓝色
133 |     void SetColor(ALogLevel level, uint32_t color) {
134 |         _color[int(level)] = color;
135 |     }
136 | 
137 | protected:
138 |     uint32_t _color[4];
139 |     static bool _isAllocateConsole;
140 |     HANDLE _handle = nullptr;
141 |     virtual void _Output(ALogLevel level, std::string&& str) override;
142 | };
143 | 
144 | class ATickRunner;
145 | template <>
146 | class ALogger : public AAbstractLogger {
147 | public:
148 |     ALogger();
149 |     __ANodiscard APainter& GetPainter() { return _painter; }
150 | 
151 |     // 设置显示颜色
152 |     // *** 使用示例:
153 |     // SetColor(ALogLevel::INFO, AArgb(0xff, 0, 0, 0xff)) ------ 将 INFO 等级的显示颜色设置为蓝色
154 |     // SetColor(ALogLevel::INFO, AArgb(0xff, 0, 0, 0xff), AArgb(0xff, 0, 0, 0)) ------ 将 INFO 等级的显示颜色设置为蓝色,并将背景色设置为黑色
155 |     void SetColor(ALogLevel level, uint32_t textColor, uint32_t bkgColor = AArgb(0xaf, 0, 0, 0)) {
156 |         _textColors[int(level)] = textColor;
157 |         _rectColors[int(level)] = bkgColor;
158 |     }
159 | 
160 |     // 设置背景颜色
161 |     // *** 使用示例:
162 |     // SetBkgColor(ALogLevel::INFO, AArgb(0xff, 0, 0, 0) ------- 将 INFO 等级的背景色设置为黑色
163 |     void SetBkgColor(ALogLevel level, uint32_t bkgColor) {
164 |         _rectColors[int(level)] = bkgColor;
165 |     }
166 | 
167 |     // 设定显示持续时间
168 |     // *** 使用示例:
169 |     // SetRemainTime(100) ------- 将显示持续时间改为 100
170 |     void SetRemainTime(int remainTime) {
171 |         _remainTime = std::clamp(remainTime, 0, INT_MAX);
172 |     }
173 | 
174 |     // 设定显示持续时间
175 |     // *** 使用示例:
176 |     // SetPos(50, 100) ------- 将显示位置更改为 (50, 100) [最大为 800, 600]
177 |     void SetPos(int x, int y) {
178 |         _pixelDisplay.x = std::clamp(x, 0, 800);
179 |         _pixelDisplay.y = std::clamp(y, 0, 600);
180 |     }
181 | 
182 |     // 设置平滑过渡速度
183 |     // 默认值为 3
184 |     void SetTransitSpeed(int speed) {
185 |         _transitSpeed = std::clamp(speed, 1, INT_MAX);
186 |     }
187 | 
188 | protected:
189 |     uint32_t _textColors[4];
190 |     uint32_t _rectColors[4];
191 |     APainter _painter;
192 |     int _remainTime = 500; // 控制显示的持续时间
193 |     APixel _pixelDisplay = {10, 500};
194 |     struct _Display {
195 |         ALogLevel level;
196 |         std::string str;
197 |         int gameClock;
198 |         int lineCnt = 0;
199 |     };
200 |     std::shared_ptr _tickRunner;
201 |     std::deque<_Display> _displayList;
202 |     int _curBottom = 0; // 为了平滑过渡使用的
203 |     int _transitSpeed = 3;
204 |     virtual void _BeforeScript() override;
205 |     virtual void _Output(ALogLevel level, std::string&& str) override;
206 |     void _ShowTick();
207 | };
208 | 
209 | template <>
210 | class ALogger : public AAbstractLogger {
211 | protected:
212 |     virtual void _Output(ALogLevel level, std::string&& str) override;
213 |     virtual void _BeforeScript() override;
214 | };
215 | 
216 | inline AAbstractLogger* aLogger;
217 | 
218 | // 注意这个函数返回的是对象指针
219 | inline AAbstractLogger* AGetInternalLogger() {
220 |     return aLogger;
221 | }
222 | 
223 | inline void ASetInternalLogger(AAbstractLogger& logger) {
224 |     aLogger = &logger;
225 | }
226 | 
227 | #endif
228 | 


--------------------------------------------------------------------------------
/inc/avz_painter.h:
--------------------------------------------------------------------------------
  1 | #ifndef __AVZ_PAINTER_H__
  2 | #define __AVZ_PAINTER_H__
  3 | 
  4 | #include "avz_state_hook.h"
  5 | #include 
  6 | #include 
  7 | #include 
  8 | #include 
  9 | #include 
 10 | #include 
 11 | #include 
 12 | 
 13 | struct __AGeoVertex {
 14 |     float __x, __y, __z, __rhw;
 15 |     DWORD __color;
 16 | 
 17 |     __AGeoVertex()
 18 |         : __x(0.0f), __y(0.0f), __z(0.0f), __rhw(0.0f), __color(0) {}
 19 | 
 20 |     __AGeoVertex(float x, float y, float z, DWORD color)
 21 |         : __x(x), __y(y), __z(z), __rhw(1.0f), __color(color) {}
 22 | 
 23 |     __AGeoVertex(int x, int y, float z, DWORD color)
 24 |         : __x((float)x), __y((float)y), __z(z), __rhw(1.0f), __color(color) {}
 25 | };
 26 | 
 27 | struct __ATexVertex {
 28 |     float __x, __y, __z, __rhw;
 29 |     DWORD __color, __spec;
 30 |     float __u, __v;
 31 | 
 32 |     __ATexVertex()
 33 |         : __x(0.0f), __y(0.0f), __z(0.0f), __rhw(0.0f), __color(0), __spec(0), __u(0), __v(0) {}
 34 | 
 35 |     __ATexVertex(float x, float y, float z,  DWORD color, float u, float v)
 36 |         : __x(x), __y(y), __z(z), __rhw(1.0f), __color(color), __spec(0), __u(u), __v(v) {}
 37 | 
 38 |     __ATexVertex(int x, int y, float z, DWORD color, float u, float v)
 39 |         : __x((float)x), __y((float)y), __z(z), __rhw(1.0f), __color(color), __spec(0), __u(u), __v(v) {}
 40 | };
 41 | 
 42 | struct __AD3dInfo {
 43 |     static IDirect3DDevice7* device;
 44 |     static IDirectDraw7* ddraw;
 45 | };
 46 | 
 47 | struct __ATextInfo : public __AD3dInfo {
 48 |     HFONT HFont;
 49 |     int fontW;
 50 |     int fontH;
 51 |     int fontG;
 52 | };
 53 | 
 54 | struct __ACursorInfo : public __AD3dInfo {
 55 |     int type;
 56 |     HCURSOR hCursor;
 57 | };
 58 | 
 59 | // 得到一个 ARGB 颜色, A 的意思是不透明度,  数值范围为 [0, 255]
 60 | // R 表示 红色, 数值范围为 [0, 255]
 61 | // G 表示 绿色, 数值范围为 [0, 255]
 62 | // B 表示 蓝色, 数值范围为 [0, 255]
 63 | // 其他颜色需要使用这三原色混合出来
 64 | // 例如完全不透明的黄色就是 AArgb(0xff, 0xff, 0xff, 0);
 65 | inline constexpr uint32_t AArgb(uint8_t a, uint8_t r, uint8_t g, uint8_t b) {
 66 |     return uint32_t(a << 24) | uint32_t(r << 16) | uint32_t(g << 8) | uint32_t(b);
 67 | }
 68 | 
 69 | class __ATexture {
 70 |     int mWidth;
 71 |     int mHeight;
 72 |     float u;
 73 |     float v;
 74 |     IDirectDrawSurface7* texture;
 75 |     __ATextInfo* textInfo = nullptr;
 76 | 
 77 | public:
 78 |     ~__ATexture();
 79 | 
 80 |     __ATexture(wchar_t chr, __ATextInfo* _textInfo);
 81 |     __ATexture(__ACursorInfo* cursorInfo);
 82 | 
 83 |     void Draw(DWORD color, int x, int y, float layer) const;
 84 | 
 85 |     IDirectDrawSurface7* CreateTextureSurface(int theWidth, int theHeight);
 86 | 
 87 | protected:
 88 |     // 给一个图像画上黑色边框
 89 |     void _DrawBlackBorder(DWORD* bits);
 90 | 
 91 |     // 将 bit 复制到 surface 上
 92 |     void _CopyBitsToSurface(DWORD* src, DDSURFACEDESC2& dst, __ACursorInfo* cursorInfo);
 93 | };
 94 | 
 95 | class __ABasicPainter : public AOrderedBeforeScriptHook<-32767>,
 96 |                         public AOrderedExitFightHook<32767>,
 97 |                         public AOrderedAfterInjectHook<-32767> {
 98 |     __ADeleteCopyAndMove(__ABasicPainter);
 99 | 
100 | public:
101 |     enum DrawType {
102 |         RECT,
103 |         TEXT,
104 |         CURSOR,
105 |     };
106 | 
107 |     struct RectInfo {
108 |         ARect rect;
109 |         DWORD color;
110 |     };
111 | 
112 |     struct TextInfo {
113 |         std::vector lines;
114 |         DWORD color;
115 |         int x;
116 |         int y;
117 |     };
118 | 
119 |     struct DrawInfo {
120 |         DrawType type;
121 |         std::variant var;
122 |         int duration;
123 |         float layer;
124 |     };
125 | 
126 |     __ABasicPainter() {
127 |         GetPainterSet().insert(this);
128 |     }
129 | 
130 |     ~__ABasicPainter() {
131 |         GetPainterSet().erase(this);
132 |     }
133 | 
134 |     // Hook
135 |     static bool AsmDraw();
136 |     static void DrawEveryTick();
137 |     static void UpdatePaintTime(); 
138 | 
139 |     void ClearFont();
140 |     std::list multiTickQueue;
141 |     std::deque singleTickQueue;
142 |     std::unordered_map> textureDict;
143 |     static std::vector> posDict;
144 | 
145 |     void DrawRect(int x, int y, int w, int h, DWORD color, float layer);
146 |     void DrawStr(const std::wstring& text, int x, int y, DWORD color, float layer);
147 |     void Draw(const DrawInfo& info);
148 |     static void DrawCursor(int x, int y, int type, float layer); // 0: 普通的 1: 手
149 |     __ATextInfo* GetTextNeedInfo();
150 |     static bool IsOpen3dAcceleration();
151 | 
152 |     static std::unordered_set<__ABasicPainter*>& GetPainterSet() {
153 |         static std::unordered_set<__ABasicPainter*> painters;
154 |         return painters;
155 |     }
156 | 
157 |     __ATextInfo textInfo;
158 |     int fontSize = 20;
159 |     std::size_t maxQueueSize = 1e4;
160 |     std::wstring fontName = L"宋体";
161 |     static HCURSOR handCursor;
162 |     static HCURSOR arrowCursor;
163 | 
164 | protected:
165 |     virtual void _BeforeScript() override;
166 |     virtual void _ExitFight() override;
167 |     virtual void _AfterInject() override;
168 | 
169 | };
170 | 
171 | class APainter {
172 |     __ADeleteCopyAndMove(APainter);
173 | 
174 | public:
175 |     APainter() = default;
176 |     // 设置字体
177 |     // 使用示例
178 |     // SetFont("黑体") ------ 将字体设置为黑体
179 |     void SetFont(const std::string& name);
180 | 
181 |     // 得到字体
182 |     __ANodiscard std::string GetFont() const;
183 | 
184 |     // 设置字体大小
185 |     // 使用示例
186 |     // SetFontSize(15) ------ 将字体大小设置为 15
187 |     // 注意此处字体大小不一定与 MS Word 中的相同
188 |     void SetFontSize(int size);
189 | 
190 |     // 得到字体的大小
191 |     __ANodiscard int GetFontSize() const;
192 | 
193 |     // 设置文本颜色
194 |     // 使用示例
195 |     // 注意每个参数范围为 [0, 255]
196 |     // SetTextColor(AArgb(0xff, 0, 0, 0)) ----- 将文本的不透明度设置为 0xff, 也就是不透明, 色彩设置为 RGB(0, 0, 0), 也就是黑色
197 |     void SetTextColor(DWORD color);
198 | 
199 |     __ANodiscard DWORD GetTextColor() const;
200 | 
201 |     // 设置矩形框颜色
202 |     // 使用示例
203 |     // 注意每个参数范围为 [0, 255]
204 |     // SetRectColor(AArgb(0xff, 0, 0, 0)) ----- 将矩形框的不透明度设置为 0xff, 也就是不透明, 色彩设置为 RGB(0, 0, 0), 也就是黑色
205 |     void SetRectColor(DWORD color);
206 | 
207 |     __ANodiscard DWORD GetRectColor() const;
208 | 
209 |     // 绘制函数
210 |     // 第一个参数指的是绘制什么: 文本还是矩形
211 |     // 第二个参数指的是绘制的持续时间
212 |     // 第三个参数是图层,类型: float,有效范围为 [0, 1]
213 |     // 绘制文本
214 |     // ***使用示例
215 |     // Draw(AText("hello", 100, 100)) ------ 在游戏画面(100, 100) 处绘制 hello
216 |     // Draw(AText("hello", 100, 100, RIGHT_TOP)) ------ 在游戏画面(100, 100) 处绘制 hello, 并且文本绘制是在 (100, 100) 的右上方
217 |     // 绘制矩形
218 |     // ***使用示例
219 |     // Draw(ARect(100, 100, 200, 200)) ------ 在游戏画面(100, 100) 处绘制宽高为 (200, 200) 的矩形, 默认显示 1cs
220 |     // Draw(ARect(100, 100, 200, 200), 100) ------ 在游戏画面(100, 100) 处绘制宽高为 (200, 200) 的矩形, 显示 100cs
221 |     void Draw(const ARect& rect, int duration = 1, float layer = 0.0f);
222 |     void Draw(const AText& posText, int duration = 1, float layer = 0.0f);
223 |     void Draw(const ACursor& cursor, int duration = 1, float layer = 0.0f);
224 | 
225 |     // 设定队列最大容量
226 |     // 这个容量是为了防止内存泄露的
227 |     // 设置的越小就会限制在屏幕中显示的数目;设置的越大就越难发现跳帧时的内存泄露
228 |     void SetMaxQueueSize(std::size_t size) {
229 |         _maxQueueSize = size;
230 |     }
231 | 
232 | protected:
233 |     DWORD _textColor = AArgb(0xff, 0, 0xff, 0xff);
234 |     DWORD _rectColor = AArgb(0xaf, 0, 0, 0);
235 |     __ABasicPainter _basicPainter;
236 |     std::size_t _maxQueueSize = 1e4;
237 | };
238 | 
239 | class ATickPainter {
240 |     __ADeleteCopyAndMove(ATickPainter);
241 | 
242 | public:
243 | 
244 | };
245 | 
246 | #endif
247 | 


--------------------------------------------------------------------------------
/inc/avz_script.h:
--------------------------------------------------------------------------------
  1 | #ifndef __AVZ_SCRIPT_H__
  2 | #define __AVZ_SCRIPT_H__
  3 | 
  4 | #include "avz_asm.h"
  5 | #include "avz_state_hook.h"
  6 | 
  7 | class __AProfiler : public AOrderedEnterFightHook<-32768> {
  8 | protected:
  9 |     void _EnterFight() override {
 10 |         avzTime.clear();
 11 |         pvzTime.clear();
 12 |         paintTime.clear();
 13 |     }
 14 | 
 15 | public:
 16 |     // AvZ 每帧的运行时间,单位为秒
 17 |     std::vector avzTime;
 18 |     // PvZ 每帧的运行时间,单位为秒
 19 |     std::vector pvzTime;
 20 |     // AvZ 每帧的绘制时间,单位为秒
 21 |     std::vector paintTime;
 22 | 
 23 |     static double CurrentTime() {
 24 |         static double scale = [] {
 25 |             LARGE_INTEGER ret;
 26 |             QueryPerformanceFrequency(&ret);
 27 |             return 1.0 / ret.QuadPart;
 28 |         }();
 29 |         LARGE_INTEGER ret;
 30 |         QueryPerformanceCounter(&ret);
 31 |         return ret.QuadPart * scale;
 32 |     }
 33 | } inline __aProfiler;
 34 | 
 35 | class __AScriptManager {
 36 | public:
 37 |     bool isBlockable = true;
 38 |     bool isLoaded = false;
 39 |     bool willBeExit = false;
 40 |     bool isExit = false;
 41 |     AReloadMode scriptReloadMode = AReloadMode::NONE;
 42 |     int blockDepth = 0;
 43 |     ATime blockTime;
 44 | 
 45 |     // 用于 EnterGame
 46 |     bool isNeedEnterGame = false;
 47 |     int gameMode = 13;
 48 |     bool hasContinueDialog = false;
 49 |     int continueCountdown = -1;
 50 |     int backToMainCountdown = -1;
 51 | 
 52 |     // 用于 BackToMain
 53 |     bool isNeedBackToMain = false;
 54 |     bool isSaveData = true;
 55 |     int enterGameCountdown = -1;
 56 | 
 57 |     void GlobalInit();
 58 |     bool MemoryInit();
 59 |     void LoadScript();
 60 |     void RunScript();
 61 |     void RunTotal();
 62 |     void ScriptHook();
 63 |     void WaitUntil(int wave, int time);
 64 |     void WaitForFight(bool isSkipTick);
 65 |     void EnterGame(int gameMode, bool hasContinueDialog);
 66 |     void BackToMain(bool isSaveData);
 67 |     void FastSaveLoad();
 68 | };
 69 | 
 70 | inline __AScriptManager __aScriptManager;
 71 | 
 72 | // 阻塞运行直到达到目标时间点
 73 | __ADeprecated("请使用 ACoroutine ACoScript() 和 co_await ATime())") inline void AWaitUntil(int wave, int time) {
 74 |     __aScriptManager.WaitUntil(wave, time);
 75 | }
 76 | 
 77 | // 设置脚本重新载入模式
 78 | // *** 使用示例
 79 | // ASetReloadMode(AReloadMode::NONE) ----- 脚本运行结束后不再重新载入
 80 | // ASetReloadMode(AReloadMode::MAIN_UI) ----- 脚本运行结束在返回主界面后重新载入
 81 | // ASetReloadMode(AReloadMode::MAIN_UI_OR_FIGHT_UI) ----- 脚本运行结束在返回主界面或战斗界面后重新载入
 82 | inline void ASetReloadMode(AReloadMode reloadMode) {
 83 |     __aScriptManager.scriptReloadMode = reloadMode;
 84 | }
 85 | 
 86 | // 等待游戏进入战斗界面释放阻塞
 87 | inline void AWaitForFight(bool isSkipTick = false) {
 88 |     __aScriptManager.WaitForFight(isSkipTick);
 89 | }
 90 | 
 91 | // 快速进入游戏函数
 92 | // *** 使用示例
 93 | // AEnterGame() ------ 默认进入泳池无尽生存模式,默认会自动点掉继续对话框
 94 | // AEnterGame(AAsm::SURVIVAL_ENDLESS_STAGE_1) -------- 进入白天无尽生存模式,默认会自动点掉继续对话框
 95 | // AEnterGame(AAsm::SURVIVAL_ENDLESS_STAGE_1, false) -------- 进入白天无尽生存模式,不会自动点掉继续对话框
 96 | inline void AEnterGame(int gameMode = AAsm::SURVIVAL_ENDLESS_STAGE_3, bool hasContinueDialog = false) {
 97 |     __aScriptManager.EnterGame(gameMode, hasContinueDialog);
 98 | }
 99 | 
100 | // 快速回到游戏主界面
101 | // *** 注意:此函数仅在战斗界面生效
102 | // *** 使用示例
103 | // ABackToMain() ----- 直接回到主界面,默认会自动存档
104 | // ABackToMain(false) ----- 直接回到主界面,不会自动存档
105 | inline void ABackToMain(bool isSaveData = true) {
106 |     __aScriptManager.BackToMain(isSaveData);
107 | }
108 | 
109 | #endif
110 | 


--------------------------------------------------------------------------------
/inc/avz_seh.h:
--------------------------------------------------------------------------------
 1 | #ifndef __SEH_H__
 2 | #define __SEH_H__
 3 | 
 4 | #define _CRT_SECURE_NO_WARNINGS 1
 5 | #include "windows.h"
 6 | #include 
 7 | #include 
 8 | 
 9 | class ASeh {
10 |     static LRESULT CALLBACK SEHWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
11 |     static long __stdcall UnhandledExceptionFilter(LPEXCEPTION_POINTERS lpExceptPtr);
12 |     static void DoHandleDebugEvent(LPEXCEPTION_POINTERS lpEP);
13 |     static bool GetLogicalAddress(void* addr, char* szModule, DWORD len, DWORD& section, DWORD& offset);
14 |     static const char* GetFilename(const char* thePath);
15 |     static void ShowErrorDialog(const char* theErrorTitle, const char* theErrorText);
16 |     static bool CheckImageHelp();
17 |     static std::string IntelWalk(PCONTEXT theContext, int theSkipCount);
18 |     static std::string ImageHelpWalk(PCONTEXT theContext, int theSkipCount);
19 | 
20 | public:
21 |     ASeh();
22 |     ~ASeh();
23 | };
24 | 
25 | inline ASeh __aSeh;
26 | 
27 | #endif
28 | 


--------------------------------------------------------------------------------
/inc/avz_smart.h:
--------------------------------------------------------------------------------
  1 | #ifndef __AVZ_SMART_H__
  2 | #define __AVZ_SMART_H__
  3 | 
  4 | #include "avz_tick_runner.h"
  5 | 
  6 | class AItemCollector : public ATickRunnerWithNoStart,
  7 |                        public AOrderedEnterFightHook<-1> {
  8 |     __ADeleteCopyAndMove(AItemCollector);
  9 | 
 10 | public:
 11 |     AItemCollector();
 12 |     void Start();
 13 |     void SetInterval(int timeInterval);
 14 | 
 15 |     // 设置收集类型
 16 |     // 1银币
 17 |     // 2金币
 18 |     // 3钻石
 19 |     // 4阳光
 20 |     // 5小阳光
 21 |     // 6大阳光
 22 |     // 8奖杯
 23 |     // 9铲子
 24 |     // 10图鉴
 25 |     // 11钥匙
 26 |     // 12透视空花瓶
 27 |     // 13绿水壶
 28 |     // 14玉米卷
 29 |     // 15信
 30 |     // 16植物卡片
 31 |     // 17礼物盒子
 32 |     // 18钱袋
 33 |     // 19礼物盒子
 34 |     // 20钱袋
 35 |     // 21银向日葵
 36 |     // 22金向日葵
 37 |     // 23巧克力
 38 |     // 24巧克力
 39 |     // 25礼物盒子
 40 |     // 26礼物盒子
 41 |     // 27礼物盒子
 42 |     // 使用示例:
 43 |     // SetTypeList({1, 2, 3}); -------- 只收集银币金币和钻石
 44 |     void SetTypeList(const std::vector& types);
 45 | 
 46 | protected:
 47 |     int _timeInterval = 10;
 48 |     static constexpr int _TYPE_SIZE = 28;
 49 |     std::array _types;
 50 |     void _Run();
 51 |     virtual void _EnterFight() override;
 52 | };
 53 | 
 54 | class AIceFiller : public ATickRunnerWithNoStart,
 55 |                    public AOrderedEnterFightHook<-1> {
 56 |     __ADeleteCopyAndMove(AIceFiller);
 57 | public:
 58 |     // 优先按格子使用还是优先按 HP 使用
 59 |     enum PriorityMode {
 60 |         GRID,
 61 |         HP,
 62 |     };
 63 | 
 64 | protected:
 65 |     std::vector _fillIceGridVec;
 66 |     std::vector _tempIceGridVec;
 67 |     std::vector _iceSeedIdxVec;
 68 |     int _seedType;
 69 |     int _coffeeSeedIdx;
 70 |     PriorityMode _priorityMode = GRID;
 71 |     void _Run();
 72 | 
 73 | public:
 74 |     AIceFiller() = default;
 75 |     // 重置冰卡
 76 |     // *** 注意:该函数需要使用在 start 函数之后才能生效
 77 |     // *** 使用示例
 78 |     // SetIceSeedList({AICE_SHROOM}) ------ 只使用原版冰
 79 |     // SetIceSeedList({AM_ICE_SHROOM, AICE_SHROOM}) ----- 优先使用模仿冰,再使用原版冰
 80 |     void SetIceSeedList(const std::vector& lst);
 81 | 
 82 |     // 重置存冰位置
 83 |     // *** 使用示例:
 84 |     // SetList({{3,4},{5,6}})-----将存冰位置重置为{3,4},{5,6}
 85 |     void SetList(const std::vector& lst) {
 86 |         _fillIceGridVec = lst;
 87 |     }
 88 | 
 89 |     const std::vector& GetList() const {
 90 |         return _fillIceGridVec;
 91 |     }
 92 | 
 93 |     // 设置存冰位的优先级模式
 94 |     // GRID: 按格子优先级使用存冰位
 95 |     // HP: 按植物血量优先级使用存冰位
 96 |     void SetPriorityMode(PriorityMode mode) {
 97 |         _priorityMode = mode;
 98 |     }
 99 | 
100 |     // 删除一个或多个存冰位
101 |     void EraseFromList(int row, int col);
102 |     void EraseFromList(const std::vector& lst);
103 | 
104 |     // 将一个或多个存冰位的优先级升至最高(最先存,最后用)
105 |     // 如果指定的存冰位不在存冰位列表中,会自动将其添加至存冰位列表
106 |     void MoveToListTop(int row, int col);
107 |     void MoveToListTop(const std::vector& lst);
108 | 
109 |     // 将一个或多个存冰位的优先级降至最低(最后存,最先用)
110 |     // 如果指定的存冰位不在存冰位列表中,会自动将其添加至存冰位列表
111 |     void MoveToListBottom(int row, int col);
112 |     void MoveToListBottom(const std::vector& lst);
113 | 
114 |     // 设置临时存冰位
115 |     // 临时存冰位的放置和使用优先级都高于永久存冰位置,但是临时存冰位使用一次后就会从列表中移除
116 |     void SetTempPositions(const std::vector& lst) {
117 |         _tempIceGridVec = lst;
118 |     }
119 | 
120 |     // 设置临时存冰位
121 |     // 临时存冰位的放置和使用优先级都高于永久存冰位置,但是临时存冰位使用一次后就会从列表中移除
122 |     void AddTempPosition(int row, int col) {
123 |         _tempIceGridVec.emplace_back(row, col);
124 |     }
125 | 
126 |     // 线程开始工作
127 |     // *** 使用示例:
128 |     // Start({{3,4},{5,6}})-----在{3,4},{5,6}位置存冰
129 |     void Start(const std::vector& lst);
130 | 
131 |     // 使用咖啡豆函数
132 |     // *** 使用示例:
133 |     // Coffee()-----自动使用执行次序低的存冰位
134 |     // Coffee(3, 1)-----使用第3行第1列的存冰,若不存在则等效于 Coffee()
135 |     void Coffee();
136 |     void Coffee(int row, int col);
137 | 
138 |     virtual void _EnterFight() override;
139 | };
140 | 
141 | class APlantFixer : public ATickRunnerWithNoStart {
142 |     __ADeleteCopyAndMove(APlantFixer);
143 | 
144 | protected:
145 |     bool _isUseCoffee = false;
146 |     int _plantType;
147 |     int _fixHp = 0;
148 |     int _coffeeSeedIdx;
149 |     std::vector _seedIdxVec;
150 |     std::vector _gridLst;
151 |     void _GetSeedList();
152 |     void _Run();
153 |     void _UseSeed(int seedIndex, int row, float col, bool isNeedShovel);
154 | 
155 | public:
156 |     APlantFixer() = default;
157 |     // 重置植物修补位置
158 |     // *** 使用示例:
159 |     // SetList({{2, 3},{3, 4}})-------位置被重置为{2,3},{3,4}
160 |     void SetList(const std::vector& lst) {
161 |         _gridLst = lst;
162 |     }
163 | 
164 |     const std::vector& GetList() const {
165 |         return _gridLst;
166 |     }
167 | 
168 |     // 删除一个或多个修补位置
169 |     void EraseFromList(int row, int col);
170 |     void EraseFromList(const std::vector& lst);
171 | 
172 |     // 将一个或多个修补位置的优先级升至最高(最先修补)
173 |     // 如果指定的修补位置不在修补位置列表中,会自动将其添加至修补位置列表
174 |     void MoveToListTop(int row, int col);
175 |     void MoveToListTop(const std::vector& lst);
176 | 
177 |     // 将一个或多个修补位置的优先级降至最低(最后修补)
178 |     // 如果指定的修补位置不在修补位置列表中,会自动将其添加至修补位置列表
179 |     void MoveToListBottom(int row, int col);
180 |     void MoveToListBottom(const std::vector& lst);
181 | 
182 |     // 自动得到修补的位置列表
183 |     void AutoSetList();
184 | 
185 |     // 自动得到修补的位置列表
186 |     __ADeprecated("请使用 AutoSetList()") void AutoGetList() {
187 |         AutoSetList();
188 |     }
189 | 
190 |     // 线程开始工作,此函数开销较大,不建议多次调用
191 |     // 第一个参数为植物类型
192 |     // 第二个参数不填默认全场
193 |     // 第三个参数不填默认植物血量为150以下时修补
194 |     // *** 使用示例:
195 |     // Start(AGJG_23)-------修补全场的高坚果
196 |     // Start(APUMPKIN, {{1,3},{2,3}})-----修补位置为{1,3},{2,3}位置的南瓜头
197 |     // Start(AJG_3,{{1,3},{2,3}},300)------修补位置为{1,3},{2,3}位置的坚果,血量降至300开始修补
198 |     void Start(int plantType, const std::vector& lst = {}, int fixHp = 150);
199 | 
200 |     // 重置修补血量
201 |     // *** 使用示例:
202 |     // SetHp(200)------将修补触发血量改为200
203 |     void SetHp(int _fix_hp) {
204 |         _fixHp = _fix_hp;
205 |     }
206 | 
207 |     // 是否使用咖啡豆
208 |     void SetIsUseCoffee(bool isUseCoffee) {
209 |         _isUseCoffee = isUseCoffee;
210 |     }
211 | };
212 | 
213 | #endif
214 | 


--------------------------------------------------------------------------------
/inc/avz_state_hook.h:
--------------------------------------------------------------------------------
  1 | #ifndef __AVZ_STATE_HOOK_H__
  2 | #define __AVZ_STATE_HOOK_H__
  3 | 
  4 | #include "avz_global.h"
  5 | 
  6 | #define __ADefineHookClass(HookName)                                                      \
  7 |     class __APublic##HookName##Hook {                                                     \
  8 |     public:                                                                               \
  9 |         using HookContainer = std::multimap;             \
 10 |         __APublic##HookName##Hook(int runOrder);                                          \
 11 |         virtual ~__APublic##HookName##Hook();                                             \
 12 |         void Run##HookName();                                                             \
 13 |         static void Reset();                                                              \
 14 |         static void RunAll();                                                             \
 15 |                                                                                           \
 16 |     protected:                                                                            \
 17 |         static HookContainer& _GetHookContainer();                                        \
 18 |         virtual void _##HookName() {};                                                    \
 19 |         HookContainer::iterator _iter;                                                    \
 20 |         bool _isRun = false;                                                              \
 21 |         static bool _isRunAll;                                                            \
 22 |     };                                                                                    \
 23 |     template                                                               \
 24 |     class AOrdered##HookName##Hook : protected __APublic##HookName##Hook {                \
 25 |     public:                                                                               \
 26 |         AOrdered##HookName##Hook()                                                        \
 27 |             : __APublic##HookName##Hook(hookOrder) {                                      \
 28 |         }                                                                                 \
 29 |         static constexpr int HOOK_ORDER = hookOrder;                                      \
 30 |     };                                                                                    \
 31 |     using A##HookName##Hook = AOrdered##HookName##Hook<0>;                                \
 32 |     template                                                               \
 33 |     __APublic##HookName##Hook& AToPublicHook(AOrdered##HookName##Hook& hook) { \
 34 |         return *((__APublic##HookName##Hook*)(&hook));                                    \
 35 |     }
 36 | 
 37 | // 此函数会在 本框架 基本内存信息初始化完成后且调用 void AScript() 之前运行
 38 | __ADefineHookClass(BeforeScript);
 39 | 
 40 | // 此函数会在 本框架 调用 void AScript() 之后运行
 41 | __ADefineHookClass(AfterScript);
 42 | 
 43 | // 此函数会在游戏进入战斗界面后立即运行
 44 | __ADefineHookClass(EnterFight);
 45 | 
 46 | // 此函数会在游戏退出战斗界面后立即运行
 47 | // 特别注意: 如果用户从主界面进入选卡界面但是又立即退回主界面,此函数依然会运行
 48 | __ADefineHookClass(ExitFight);
 49 | 
 50 | // 此函数会在每次注入之后运行
 51 | // 注意此函数非常危险,此函数内无法使用很多 AvZ 的功能,至于无法使用哪些,
 52 | // 用户可以自行踩雷,因为实在是太多了,不想一一枚举
 53 | // 因为 AvZ 的初始化发生在进入战斗界面或者选卡界面的时候
 54 | __ADefineHookClass(AfterInject);
 55 | 
 56 | // 此函数会在每帧运行 AvZ 主体代码之前运行
 57 | // 注意此函数非常危险,最好判断一下当前 PvZ 的状态再使用 AvZ 的内置函数
 58 | __ADefineHookClass(BeforeTick);
 59 | 
 60 | // 此函数会在每帧运行 AvZ 主体代码之后运行
 61 | // 注意此函数非常危险,最好判断一下当前 PvZ 的状态再使用 AvZ 的内置函数
 62 | __ADefineHookClass(AfterTick);
 63 | 
 64 | // 此函数在 AvZ 因异常退出或者卸载前运行
 65 | __ADefineHookClass(BeforeExit);
 66 | 
 67 | template 
 68 | class __APublicStateHookT : public Types... {
 69 | public:
 70 |     __APublicStateHookT(int hookOrder)
 71 |         : Types(hookOrder)... {
 72 |     }
 73 | };
 74 | 
 75 | using __APublicStateHook = __APublicStateHookT<
 76 |     __APublicBeforeScriptHook,
 77 |     __APublicAfterScriptHook,
 78 |     __APublicEnterFightHook,
 79 |     __APublicExitFightHook,
 80 |     __APublicBeforeTickHook,
 81 |     __APublicAfterTickHook,
 82 |     __APublicAfterInjectHook,
 83 |     __APublicBeforeExitHook>;
 84 | 
 85 | template 
 86 | class AOrderedStateHook : protected __APublicStateHook {
 87 | public:
 88 |     AOrderedStateHook()
 89 |         : __APublicStateHook(hookOrder) {
 90 |     }
 91 |     static constexpr int HOOK_ORDER = hookOrder;
 92 | };
 93 | 
 94 | template 
 95 | __APublicStateHook& AToPublicHook(AOrderedStateHook& hook) {
 96 |     return *((__APublicStateHook*)(&hook));
 97 | }
 98 | 
 99 | using AStateHook = AOrderedStateHook<0>;
100 | 
101 | template 
102 |     requires(FirstHook::HOOK_ORDER < INT_MAX)
103 | class __AMaxHookT {
104 | public:
105 |     static constexpr int VALUE = std::max(FirstHook::HOOK_ORDER, __AMaxHookT::VALUE);
106 | };
107 | 
108 | template 
109 |     requires(FirstHook::HOOK_ORDER < INT_MAX)
110 | class __AMaxHookT {
111 | public:
112 |     static constexpr int VALUE = FirstHook::HOOK_ORDER;
113 | };
114 | 
115 | template 
116 | constexpr int AAfterHook = __AMaxHookT::VALUE + 1;
117 | 
118 | template 
119 |     requires(FirstHook::HOOK_ORDER > INT_MIN)
120 | class __AMinHookT {
121 | public:
122 |     static constexpr int VALUE = std::min(FirstHook::HOOK_ORDER, __AMaxHookT::VALUE);
123 | };
124 | 
125 | template 
126 |     requires(FirstHook::HOOK_ORDER > INT_MIN)
127 | class __AMinHookT {
128 | public:
129 |     static constexpr int VALUE = FirstHook::HOOK_ORDER;
130 | };
131 | 
132 | template 
133 | constexpr int ABeforeHook = __AMinHookT::VALUE - 1;
134 | 
135 | #endif
136 | 


--------------------------------------------------------------------------------
/inc/avz_time_queue.h:
--------------------------------------------------------------------------------
  1 | #ifndef __AVZ_TIME_QUEUE_H__
  2 | #define __AVZ_TIME_QUEUE_H__
  3 | 
  4 | #include "avz_global.h"
  5 | #include "avz_logger.h"
  6 | #include "avz_memory.h"
  7 | #include 
  8 | #include 
  9 | 
 10 | struct __ABoolOperation {
 11 |     AOperation operation;
 12 |     bool isStopped = false;
 13 | 
 14 |     __ABoolOperation(const AOperation& operation)
 15 |         : operation(operation) {}
 16 | 
 17 |     __ABoolOperation(AOperation&& operation)
 18 |         : operation(std::move(operation)) {}
 19 | };
 20 | 
 21 | struct __ATimeOperation {
 22 |     AOperation operation;
 23 |     ATime time;
 24 | 
 25 |     __ATimeOperation(const AOperation& operation, const ATime& time)
 26 |         : operation(operation), time(time) {}
 27 | 
 28 |     __ATimeOperation(AOperation&& operation, const ATime& time)
 29 |         : operation(std::move(operation)), time(time) {}
 30 | };
 31 | 
 32 | struct __AOperationQueue {
 33 |     using RunOrderQueue = std::multimap;
 34 |     constexpr static int UNINIT = INT_MIN;
 35 |     RunOrderQueue queue;
 36 |     int calRefreshTime = UNINIT; // 计算得到的刷新时间,操作队列都用此时间
 37 |     int memRefreshTime = UNINIT; // 通过读内存得到的真实刷新时间,用于检查用户设定的波长是否正确
 38 |     int waveLength = -1;
 39 | };
 40 | 
 41 | using __ATimeIter = __AOperationQueue::RunOrderQueue::iterator;
 42 | 
 43 | class __AOpQueueManager : public AOrderedBeforeScriptHook<-32768>,
 44 |                           public AOrderedEnterFightHook<-32768>,
 45 |                           public AOrderedExitFightHook<-32768> {
 46 | public:
 47 |     bool isInitialized = false;
 48 |     std::vector<__AOperationQueue> queues;
 49 |     ATime startTime; // 脚本设定的开始时间
 50 |     int totalWave;
 51 |     std::optional<__ATimeIter> Push(const ATime& time, __ABoolOperation&& timeOp);
 52 |     void UpdateRefreshTime();
 53 |     void SetWavelength(const std::vector& lst);
 54 |     void AssumeWavelength(const std::vector& lst);
 55 |     void RunOperation();
 56 | 
 57 | protected:
 58 |     static std::optional _GetNextWaveCountdown();
 59 |     void _PrintLog(const ATime& time, int nowTime);
 60 |     bool _CheckWavelength(const ATime& time);
 61 |     void _CheckAssumeWavelength(int wave);
 62 |     void _CalculateRefreshTime(int startWave);
 63 |     void _RecordRefresh(int wave, int refreshTime);
 64 |     virtual void _BeforeScript() override;
 65 |     virtual void _EnterFight() override;
 66 |     virtual void _ExitFight() override;
 67 | } inline __aOpQueueManager;
 68 | 
 69 | // 得到当前游戏的波数
 70 | __ANodiscard int ANowWave();
 71 | __ANodiscard int ANowWave(bool allowNegativeTime);
 72 | 
 73 | // 得到当前时间,读取失败返回 INT_MIN
 74 | // *** 注意得到的是以参数波刷新时间点为基准的相对时间
 75 | // *** 使用示例:
 76 | // ANowTime(1) -------- 得到以第一波刷新时间点为基准的当前时间
 77 | // ANowTime(2) -------- 得到以第二波刷新时间点为基准的当前时间
 78 | __ANodiscard int ANowTime(int wave);
 79 | __ANodiscard ATime ANowTime();
 80 | __ANodiscard ATime ANowTime(bool allowNegativeTime);
 81 | 
 82 | // 得到当前时间的延迟时间
 83 | __ANodiscard ATime ANowDelayTime(int delayTime);
 84 | 
 85 | // 设定特定波的波长
 86 | // *** 注意: 对最后一波无效
 87 | // 第 9、19、... 波的合法波长范围为 1346 - 5245,其他波为 601 - 3100
 88 | // *** 使用示例:
 89 | // ASetWavelength({ATime(1, 601), ATime(4, 1000)}) ----- 将第一波的波长设置为 601,将第四波的波长设置为 1000
 90 | inline void ASetWavelength(const std::vector& lst) {
 91 |     __aOpQueueManager.SetWavelength(lst);
 92 | }
 93 | 
 94 | inline void ASetWavelength(int wave, int waveLength) {
 95 |     __aOpQueueManager.SetWavelength({{wave, waveLength}});
 96 | }
 97 | 
 98 | // 假定特定波的波长
 99 | // 本函数与 ASetWavelength 区别在于, 本函数不会对内存进行修改
100 | // 只是可以让时间点的书写范围小于 -200, 如果真实的波长与假定的波长不一致, 则会报错
101 | // *** 使用示例:
102 | // AAssumeWavelength({ATime(1, 601), ATime(4, 1000)}) ----- 将第一波的波长假定为 601,将第四波的波长假定为 1000
103 | inline void AAssumeWavelength(const std::vector& lst) {
104 |     __aOpQueueManager.AssumeWavelength(lst);
105 | }
106 | 
107 | inline void AAssumeWavelength(int wave, int waveLength) {
108 |     __aOpQueueManager.AssumeWavelength({{wave, waveLength}});
109 | }
110 | 
111 | #endif
112 | 


--------------------------------------------------------------------------------
/inc/avz_timeline.h:
--------------------------------------------------------------------------------
  1 | #ifndef __AVZ_TIMELINE_H__
  2 | #define __AVZ_TIMELINE_H__
  3 | 
  4 | #include "avz_connector.h"
  5 | #include 
  6 | 
  7 | #define __ANoDiscardTimeline [[nodiscard("ATimeline 需要绑定到绝对时间才会执行")]]
  8 | 
  9 | template 
 10 | concept __AIsTimelineHook = std::is_invocable_v;
 11 | 
 12 | class __ANoDiscardTimeline ATimeline {
 13 | protected:
 14 |     using TimelineHook = std::function;
 15 | 
 16 |     struct Entry {
 17 |         ATimeOffset offset;
 18 |         std::variant action;
 19 | 
 20 |         Entry(ATimeOffset offset, auto&& action)
 21 |             : offset(offset)
 22 |             , action(std::forward(action)) {
 23 |         }
 24 |     };
 25 | 
 26 |     std::vector _entries;
 27 | 
 28 | public:
 29 |     ATimeline() = default;
 30 | 
 31 |     ATimeline(__AIsOperation auto&& action) {
 32 |         _entries.emplace_back(0, std::forward(action));
 33 |     }
 34 | 
 35 |     ATimeline(__AIsCoroutineOp auto&& action) {
 36 |         _entries.emplace_back(0, ACoFunctor(std::forward(action)));
 37 |     }
 38 | 
 39 |     ATimeline(__AIsTimelineHook auto&& hook) {
 40 |         _entries.emplace_back(0, std::forward(hook));
 41 |     }
 42 | 
 43 |     ATimeline(ATimeOffset offset, __AIsOperation auto&& action) {
 44 |         _entries.emplace_back(offset, std::forward(action));
 45 |     }
 46 | 
 47 |     ATimeline(ATimeOffset offset, __AIsCoroutineOp auto&& action) {
 48 |         _entries.emplace_back(offset, ACoFunctor(std::forward(action)));
 49 |     }
 50 | 
 51 |     ATimeline(ATimeOffset offset, __AIsTimelineHook auto&& hook) {
 52 |         _entries.emplace_back(offset, std::forward(hook));
 53 |     }
 54 | 
 55 |     ATimeline(ATimeOffset offset, const ATimeline& timeline) {
 56 |         _entries.reserve(timeline._entries.size());
 57 |         for (auto&& entry : timeline._entries)
 58 |             _entries.emplace_back(offset + entry.offset, entry.action);
 59 |     }
 60 | 
 61 |     ATimeline(ATimeOffset offset, ATimeline&& timeline) {
 62 |         _entries.reserve(timeline._entries.size());
 63 |         for (auto&& entry : timeline._entries)
 64 |             _entries.emplace_back(offset + entry.offset, std::move(entry.action));
 65 |     }
 66 | 
 67 |     ATimeline(std::initializer_list timelines) {
 68 |         size_t size = 0;
 69 |         for (auto&& timeline : timelines)
 70 |             size += timeline._entries.size();
 71 |         _entries.reserve(size);
 72 |         for (auto&& timeline : timelines)
 73 |             _entries.insert(_entries.end(), timeline._entries.begin(), timeline._entries.end());
 74 |     }
 75 | 
 76 |     ATimeline& operator&=(const ATimeline& rhs) {
 77 |         _entries.reserve(_entries.size() + rhs._entries.size());
 78 |         _entries.insert(_entries.end(), rhs._entries.begin(), rhs._entries.end());
 79 |         return *this;
 80 |     }
 81 | 
 82 |     __ADeprecated("请使用 & 运算符合并 ATimeline")
 83 |     ATimeline& operator+=(const ATimeline& rhs) {
 84 |         return *this &= rhs;
 85 |     }
 86 | 
 87 |     ATimeline Offset(ATimeOffset offset) const {
 88 |         return ATimeline(offset, *this);
 89 |     }
 90 | 
 91 |     ATimeline Offset(int wave, int time) const {
 92 |         return ATimeline(ATimeOffset(wave, time), *this);
 93 |     }
 94 | 
 95 |     ATimeline& operator+=(ATimeOffset offset) {
 96 |         for (auto&& entry : _entries)
 97 |             entry.offset += offset;
 98 |         return *this;
 99 |     }
100 | 
101 |     ATimeline& operator-=(ATimeOffset offset) {
102 |         for (auto&& entry : _entries)
103 |             entry.offset -= offset;
104 |         return *this;
105 |     }
106 | 
107 |     // 获取当前时间轴的最早偏移量
108 |     // 如果时间轴为空,则返回 0
109 |     ATimeOffset GetMinOffset() const {
110 |         if (_entries.empty())
111 |             return 0;
112 |         ATimeOffset minOffset = _entries[0].offset;
113 |         for (auto& entry : _entries)
114 |             minOffset = std::min(minOffset, entry.offset);
115 |         return minOffset;
116 |     }
117 | 
118 |     friend std::vector AConnect(const ATime& time, const ATimeline& timeline) {
119 |         std::vector handles;
120 |         for (auto&& entry : timeline._entries) {
121 |             ATime entryTime = time + entry.offset;
122 |             if (auto action = std::get_if(&entry.action))
123 |                 handles.push_back(AConnect(entryTime, *action));
124 |             else if (auto hook = std::get_if(&entry.action))
125 |                 std::invoke(*hook, entryTime);
126 |         }
127 |         return handles;
128 |     }
129 | 
130 |     friend auto AConnect(const auto& condition, const ATimeline& timeline) {
131 |         return AConnect(condition, [=] {
132 |             AConnect(ANowTime(), timeline);
133 |         });
134 |     }
135 | };
136 | 
137 | inline ATimeline operator&(const ATimeline& lhs, const ATimeline& rhs) {
138 |     return {lhs, rhs};
139 | }
140 | 
141 | __ADeprecated("请使用 & 运算符合并 ATimeline")
142 | inline ATimeline operator+(const ATimeline& lhs, const ATimeline& rhs) {
143 |     return {lhs, rhs};
144 | }
145 | 
146 | inline ATimeline operator+(const ATimeline& timeline, ATimeOffset offset) {
147 |     return timeline.Offset(offset);
148 | }
149 | 
150 | inline ATimeline operator+(ATimeOffset offset, const ATimeline& timeline) {
151 |     return timeline.Offset(offset);
152 | }
153 | 
154 | inline ATimeline operator-(const ATimeline& timeline, ATimeOffset offset) {
155 |     return timeline.Offset(-offset);
156 | }
157 | 
158 | using ARelOp __ADeprecated("请使用 ATimeline") = ATimeline;
159 | 
160 | #endif
161 | 


--------------------------------------------------------------------------------
/inc/dsl/shorthand.h:
--------------------------------------------------------------------------------
1 | #include "shorthand_240713.h"
2 | 


--------------------------------------------------------------------------------
/inc/libavz.h:
--------------------------------------------------------------------------------
 1 | #ifndef __LIBAVZ_H__
 2 | #define __LIBAVZ_H__
 3 | 
 4 | #define __AVZ_VERSION__ 250823
 5 | 
 6 | #include "avz_asm.h"
 7 | #include "avz_card.h"
 8 | #include "avz_click.h"
 9 | #include "avz_cob_manager.h"
10 | #include "avz_connector.h"
11 | #include "avz_exception.h"
12 | #include "avz_game_controllor.h"
13 | #include "avz_iterator.h"
14 | #include "avz_memory.h"
15 | #include "avz_replay.h"
16 | #include "avz_script.h"
17 | #include "avz_seh.h"
18 | #include "avz_smart.h"
19 | #include "avz_time_queue.h"
20 | #include "avz_timeline.h"
21 | 
22 | inline AItemCollector aItemCollector;
23 | extern AIceFiller aIceFiller;
24 | extern APlantFixer aPlantFixer;
25 | extern ACobManager aCobManager;
26 | extern AAliveFilter aAliveZombieFilter;
27 | extern AAliveFilter aAlivePlantFilter;
28 | extern AAliveFilter aAliveSeedFilter;
29 | extern AAliveFilter aAliveItemFilter;
30 | extern AAliveFilter aAlivePlaceItemFilter;
31 | extern APainter aPainter;
32 | 
33 | #endif
34 | 


--------------------------------------------------------------------------------
/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 |     "version": 250823,
3 |     "compileOptions": "-m32 -static -std=c++2b -fexperimental-library -Werror=return-type -Werror=unused-result __CUSTOM_ARGS__ \"__FILE_NAME__\" -isystem \"__AVZ_DIR__/inc\" -lavz -lgdi32 -ldbghelp -L \"__AVZ_DIR__/bin\" -shared -o \"bin/libavz.dll\""
4 | }
5 | 


--------------------------------------------------------------------------------
/release/env1/2020_07_10.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2020_07_10.zip


--------------------------------------------------------------------------------
/release/env1/2020_07_27.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2020_07_27.zip


--------------------------------------------------------------------------------
/release/env1/2020_08_10.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2020_08_10.zip


--------------------------------------------------------------------------------
/release/env1/2020_09_15.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2020_09_15.zip


--------------------------------------------------------------------------------
/release/env1/2020_11_01.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2020_11_01.zip


--------------------------------------------------------------------------------
/release/env1/2020_12_26.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2020_12_26.zip


--------------------------------------------------------------------------------
/release/env1/2021_02_11.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2021_02_11.zip


--------------------------------------------------------------------------------
/release/env1/2021_04_19.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2021_04_19.zip


--------------------------------------------------------------------------------
/release/env1/2021_08_20.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2021_08_20.zip


--------------------------------------------------------------------------------
/release/env1/2021_12_12.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2021_12_12.zip


--------------------------------------------------------------------------------
/release/env1/2022_02_13.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2022_02_13.zip


--------------------------------------------------------------------------------
/release/env1/2022_06_30.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2022_06_30.zip


--------------------------------------------------------------------------------
/release/env1/2022_10_01.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env1/2022_10_01.zip


--------------------------------------------------------------------------------
/release/env2/2.0.0_2022_12_08.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.0.0_2022_12_08.zip


--------------------------------------------------------------------------------
/release/env2/2.1.0_2023_01_19.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.1.0_2023_01_19.zip


--------------------------------------------------------------------------------
/release/env2/2.2.6_2023_04_05.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.2.6_2023_04_05.zip


--------------------------------------------------------------------------------
/release/env2/2.3.3_2023_05_01.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.3.3_2023_05_01.zip


--------------------------------------------------------------------------------
/release/env2/2.4.4_2023_06_15.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.4.4_2023_06_15.zip


--------------------------------------------------------------------------------
/release/env2/2.5.1_2023_09_26.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.5.1_2023_09_26.zip


--------------------------------------------------------------------------------
/release/env2/2.6.0_2024_01_13.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.6.0_2024_01_13.zip


--------------------------------------------------------------------------------
/release/env2/2.7.0_2024_02_05.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.7.0_2024_02_05.zip


--------------------------------------------------------------------------------
/release/env2/2.7.1_2024_05_10.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.7.1_2024_05_10.zip


--------------------------------------------------------------------------------
/release/env2/2.8.0_2024_07_13.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.8.0_2024_07_13.zip


--------------------------------------------------------------------------------
/release/env2/2.8.1_2024_08_16.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.8.1_2024_08_16.zip


--------------------------------------------------------------------------------
/release/env2/2.8.2_2024_10_30.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.8.2_2024_10_30.zip


--------------------------------------------------------------------------------
/release/env2/2.8.3_2025_02_25.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.8.3_2025_02_25.zip


--------------------------------------------------------------------------------
/release/env2/2.8.4_2025_05_15.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.8.4_2025_05_15.zip


--------------------------------------------------------------------------------
/release/env2/2.8.5_2025_07_11.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.8.5_2025_07_11.zip


--------------------------------------------------------------------------------
/release/env2/2.8.6_2025_08_23.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2.8.6_2025_08_23.zip


--------------------------------------------------------------------------------
/release/env2/2022_02_13.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2022_02_13.zip


--------------------------------------------------------------------------------
/release/env2/2022_06_30.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2022_06_30.zip


--------------------------------------------------------------------------------
/release/env2/2022_10_01.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2022_10_01.zip


--------------------------------------------------------------------------------
/release/env2/2022_11_24_2.0.0_preview.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/release/env2/2022_11_24_2.0.0_preview.zip


--------------------------------------------------------------------------------
/release/version.txt:
--------------------------------------------------------------------------------
 1 | env2/2.8.6_2025_08_23.zip
 2 | env2/2.8.5_2025_07_11.zip
 3 | env2/2.8.4_2025_05_15.zip
 4 | env2/2.8.3_2025_02_25.zip
 5 | env2/2.8.2_2024_10_30.zip
 6 | env2/2.8.1_2024_08_16.zip
 7 | env2/2.8.0_2024_07_13.zip
 8 | env2/2.7.0_2024_02_05.zip
 9 | env2/2.6.0_2024_01_13.zip
10 | env2/2.5.1_2023_09_26.zip
11 | env2/2.4.4_2023_06_15.zip
12 | env2/2.3.3_2023_05_01.zip
13 | env2/2.2.6_2023_04_05.zip
14 | env2/2.1.0_2023_01_19.zip
15 | env2/2.0.0_2022_12_08.zip
16 | env2/2022_10_01.zip
17 | env2/2022_06_30.zip
18 | env2/2022_02_13.zip
19 | env1/2022_10_01.zip
20 | env1/2022_06_30.zip
21 | env1/2022_02_13.zip
22 | env1/2021_12_12.zip
23 | env1/2021_08_20.zip
24 | env1/2021_04_19.zip
25 | env1/2021_02_11.zip
26 | 


--------------------------------------------------------------------------------
/src/avz_click.cpp:
--------------------------------------------------------------------------------
 1 | #include "libavz.h"
 2 | 
 3 | void AClickSeed(int seedIndex) {
 4 |     auto seed = AGetMainObject()->SeedArray() + seedIndex - 1;
 5 |     AAsm::MouseClick(int(seed->Abscissa() + seed->Width() / 2),
 6 |         int(seed->Ordinate() + seed->Height() / 2), 1);
 7 | }
 8 | 
 9 | void ALeftClick(int x, int y) {
10 |     AAsm::MouseClick(x, y, 1);
11 | }
12 | 
13 | void AShovel(int row, float col, int targetType) {
14 |     if (targetType >= AM_PEASHOOTER) {
15 |         AShovel(row, col, AIMITATOR);
16 |         targetType -= AM_PEASHOOTER;
17 |     }
18 | 
19 |     AAsm::ReleaseMouse();
20 |     auto [x, y] = AGridToCoordinate(row, col);
21 |     if (targetType == ACOFFEE_BEAN)
22 |         y -= 30;
23 |     else if (targetType == -2 || targetType == APUMPKIN)
24 |         y += 30;
25 | 
26 |     if (targetType >= 0 && !AGetPlantPtr(row, col, targetType))
27 |         return;
28 | 
29 |     aLogger->Info("Shovel {}", APosition(row, col));
30 |     for (int i = 0; i < 10; ++i)
31 |         ALeftClick(x, y);
32 |     AAsm::ReleaseMouse();
33 |     AAsm::ShovelPlant(x, y);
34 |     AAsm::ReleaseMouse();
35 | }
36 | 
37 | void AShovel(int row, float col, bool pumpkin) {
38 |     AShovel(row, col, pumpkin ? -2 : -1);
39 | }
40 | 
41 | void AShovel(const std::vector& lst) {
42 |     for (auto&& crood : lst)
43 |         AShovel(crood.row, crood.col, crood.targetType);
44 | }
45 | 
46 | // 将格子转换成坐标
47 | void AGridToCoordinate(int row, float col, int& x, int& y) {
48 |     int tCol = std::clamp(int(col + 0.5), 1, 10);
49 |     x = int(col * 80.0 + 1e-3);
50 |     y = AAsm::GridToOrdinate(row - 1, tCol - 1) + 40;
51 |     if (row <= 0 || x < 0 || x >= 800 || y < 0 || y >= 600)
52 |         aLogger->Error("您输入的格子位置参数: {} 已溢出, 已帮您自动调整为边界值", APosition(row, col));
53 |     x = std::clamp(x, 0, 799);
54 |     y = std::clamp(y, 0, 599);
55 | }
56 | 
57 | std::pair AGridToCoordinate(int row, float col) {
58 |     int x;
59 |     int y;
60 |     AGridToCoordinate(row, col, x, y);
61 |     return {x, y};
62 | }
63 | 
64 | void AClickGrid(int row, float col, int offset) {
65 |     int x = 0;
66 |     int y = 0;
67 |     col = int(col + 0.5);
68 |     AGridToCoordinate(row, col, x, y);
69 |     y += offset;
70 |     ALeftClick(x, y);
71 | }
72 | 


--------------------------------------------------------------------------------
/src/avz_coroutine.cpp:
--------------------------------------------------------------------------------
 1 | #include "libavz.h"
 2 | 
 3 | std::unordered_set __ACoHandleManager::_handleSet;
 4 | 
 5 | void __ACoHandleManager::_ExitFight() {
 6 |     // 唤醒所有协程
 7 |     for (auto&& handle : _handleSet)
 8 |         std::coroutine_handle<>::from_address(handle).destroy();
 9 |     _handleSet.clear();
10 | }
11 | 
12 | bool __AWait::await_ready() const {
13 |     // 检测一下是否真的需要将协程挂起
14 |     if (_time.wave == -1) { // pred 形式
15 |         if (_predication())
16 |             return true;
17 |     } else { // time 形式
18 |         ATime nowTime = ATime(_time.wave, ANowTime(_time.wave));
19 |         if (nowTime.time > _time.time)
20 |             aLogger->Warning("co_await 等待的时间为 {}, 但是现在时间已到 {}", _time, nowTime);
21 |         if (nowTime.time >= _time.time)
22 |             return true;
23 |     }
24 |     return false;
25 | }
26 | 
27 | void __AWait::await_resume() {
28 |     // 不在战斗界面直接抛出异常
29 |     // 这会直接让协程停止运行
30 |     if (AGetPvzBase()->MainObject() == nullptr || AGetPvzBase()->GameUi() != 3)
31 |         AExitFight();
32 | }
33 | 
34 | void __AWait::await_suspend(std::coroutine_handle<> handle) {
35 |     AWaitForFight();
36 |     __ACoHandleManager::Add(handle);
37 |     if (_time.wave == -1) { // pred 形式
38 |         auto tickRunner = std::make_shared();
39 |         tickRunner->Start([handle, pred = std::move(_predication), tickRunner] {
40 |             if(pred()) {
41 |                 __ACoHandleManager::Remove(handle);
42 |                 handle.resume();
43 |                 tickRunner->Stop();
44 |             } }, false);
45 |     } else { // time 形式
46 |         auto func = [handle] {
47 |             __ACoHandleManager::Remove(handle);
48 |             handle.resume();
49 |         };
50 |         if (!AConnect(_time, func))
51 |             func();
52 |     }
53 | }
54 | 
55 | void ACoFunctor::operator()() {
56 |     if (_functor == nullptr)
57 |         return;
58 |     auto co = (*_functor)();
59 |     co.SetPtr(_functor);
60 | }
61 | 


--------------------------------------------------------------------------------
/src/avz_game_controllor.cpp:
--------------------------------------------------------------------------------
 1 | #include "libavz.h"
 2 | 
 3 | namespace {
 4 | APainter _rectPainter;
 5 | }
 6 | 
 7 | // 保存原本的机器码
 8 | uint16_t __AGameControllor::_oriAsm = 0;
 9 | 
10 | __AGameControllor::__AGameControllor() {
11 |     // 保存原本的机器码
12 |     _oriAsm = AMRef(_UPDATE_ASM_ADDR_BEGIN);
13 | 
14 |     // 初始化高级暂停的工具信息
15 |     RECT rect;
16 |     GetClientRect(AGetPvzBase()->MRef(0x350), &rect);
17 |     _pvzHeight = rect.bottom - rect.top;
18 |     _pvzWidth = rect.right - rect.left;
19 |     _rectPainter.SetRectColor(_rectColor);
20 | }
21 | 
22 | bool __AGameControllor::_CheckSkipTick() {
23 |     if (isAdvancedPaused) {
24 |         aLogger->Error("开启高级暂停或者暂停时不能启用跳帧");
25 |         return false;
26 |     }
27 | 
28 |     if (isSkipTick()) {
29 |         aLogger->Error("请等待上一个跳帧条件达到后的下一帧再设定跳帧条件");
30 |         return false;
31 |     }
32 | 
33 |     return true;
34 | }
35 | 
36 | void __AGameControllor::SkipTick(int wave, int time) {
37 |     SkipTick([=]() {
38 |         int nowTime = ANowTime(wave);
39 |         if (nowTime == __AOperationQueue::UNINIT || nowTime < time) { // 时间未到达
40 |             return true;
41 |         }
42 |         if (nowTime > time) { // 时间已到达
43 |             aLogger->Error("无法回跳时间点");
44 |         }
45 |         return false;
46 |     });
47 | }
48 | 
49 | void __AGameControllor::SetAdvancedPause(bool isAdvancedPaused, bool isPlaySound, DWORD rectColor) {
50 |     if (this->isAdvancedPaused == isAdvancedPaused) {
51 |         return;
52 |     }
53 |     if (isAdvancedPaused && AGetPvzBase()->GameUi() != 3) {
54 |         aLogger->Error("高级暂停仅能在战斗界面使用");
55 |         return;
56 |     }
57 |     this->isAdvancedPaused = isAdvancedPaused;
58 |     auto asmCode = isAdvancedPaused ? _JMP_ASM : _oriAsm;
59 |     auto soundIdx = isAdvancedPaused ? 0x15 : 0x3A;
60 |     AMRef(_UPDATE_ASM_ADDR_BEGIN) = asmCode;
61 |     if (isPlaySound) {
62 |         AAsm::PlaySample(soundIdx);
63 |     }
64 |     _rectPainter.SetRectColor(rectColor);
65 | }
66 | 
67 | void __AGameControllor::SetUpdateWindow(bool isUpdateWindow) {
68 |     this->isUpdateWindow = isUpdateWindow;
69 | }
70 | 
71 | void __AGameControllor::UpdateAdvancedPause() {
72 |     _rectPainter.Draw(ARect(0, 0, _pvzWidth, _pvzHeight));
73 |     AAsm::UpdateCursorObjectAndPreview();
74 |     --AGetMainObject()->GlobalClock();
75 |     --AGetPvzBase()->MjClock();
76 |     int levelId = AGetPvzBase()->LevelId(); // 关卡序号
77 |     if (AAsm::PUZZLE_I_ZOMBIE_1 <= levelId && levelId <= AAsm::PUZZLE_I_ZOMBIE_ENDLESS) {
78 |         // 刷新卡片数组
79 |         AAsm::RefreshAllSeedPackets();
80 |         // 因为 IZE 模式的僵尸渲染位置有问题,
81 |         // 所以需要更新僵尸位置
82 |         for (auto& zombie : aAliveZombieFilter) {
83 |             zombie.MRef(0x8) = zombie.Abscissa();
84 |             zombie.MRef(0xC) = zombie.Ordinate();
85 |         }
86 |     }
87 | }
88 | 
89 | void __AGameControllor::_ExitFight() {
90 |     isSkipTick = [] {
91 |         return false;
92 |     };
93 |     // 恢复原函数调用
94 |     AMRef(_UPDATE_ASM_ADDR_BEGIN) = _oriAsm;
95 |     isUpdateWindow = true;
96 | }
97 | 


--------------------------------------------------------------------------------
/src/avz_global.cpp:
--------------------------------------------------------------------------------
 1 | #include "libavz.h"
 2 | #include 
 3 | 
 4 | __AInternalGlobal __aig;
 5 | 
 6 | bool ARangeIn(int num, const std::vector& lst) {
 7 |     for (auto _num : lst)
 8 |         if (_num == num)
 9 |             return true;
10 |     return false;
11 | }
12 | 
13 | std::wstring AStrToWstr(const std::string& input) {
14 |     std::wstring wstr;
15 |     wstr.resize(MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.length(), NULL, 0) + 1);
16 |     MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.length(), wstr.data(), wstr.size());
17 |     for (; !wstr.empty() && wstr.back() == 0;)
18 |         wstr.pop_back();
19 |     return wstr;
20 | }
21 | 
22 | std::string AWStrToStr(const std::wstring& input) {
23 |     std::string str;
24 |     str.resize(WideCharToMultiByte(CP_UTF8, 0, input.c_str(), input.length(), NULL, 0, NULL, FALSE) + 1);
25 |     WideCharToMultiByte(CP_UTF8, 0, input.c_str(), input.length(), str.data(), str.size(), NULL, FALSE);
26 |     for (; !str.empty() && str.back() == 0;)
27 |         str.pop_back();
28 |     return str;
29 | }
30 | 
31 | std::u32string AStrToU32str(const std::string& input) {
32 |     std::wstring_convert, char32_t> converter;
33 |     return converter.from_bytes(input);
34 | }
35 | 
36 | std::string AU32StrToStr(const std::u32string& input) {
37 |     std::wstring_convert, char32_t> converter;
38 |     return converter.to_bytes(input);
39 | }
40 | 


--------------------------------------------------------------------------------
/src/avz_hook.cpp:
--------------------------------------------------------------------------------
 1 | #include "libavz.h"
 2 | 
 3 | extern "C" __declspec(dllexport) void __cdecl __AScriptHook() {
 4 |     __aScriptManager.ScriptHook();
 5 | }
 6 | 
 7 | void __AInstallHook() {
 8 |     DWORD temp;
 9 |     // 54BACD 地址 call 了这个虚函数
10 |     VirtualProtect((void*)0x400000, 0x35E000, PAGE_EXECUTE_READWRITE, &temp);
11 |     *(uint32_t*)0x667bc0 = (uint32_t)&__AScriptHook;
12 | }
13 | 
14 | void __AUninstallHook() {
15 |     DWORD temp;
16 |     VirtualProtect((void*)0x400000, 0x35E000, PAGE_EXECUTE_READWRITE, &temp);
17 |     *(uint32_t*)0x667bc0 = 0x452650;
18 | }
19 | 
20 | BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
21 | 
22 |     switch (fdwReason) {
23 |     case DLL_PROCESS_ATTACH:
24 |         // attach to process
25 |         // return FALSE to fail DLL load
26 |         __aig.hInstance = hinstDLL;
27 |         __AInstallHook();
28 |         break;
29 | 
30 |     case DLL_PROCESS_DETACH:
31 |         // detach from process
32 |         __aScriptManager.willBeExit = true;
33 |         for (int i = 0; !__aScriptManager.isExit && i < 50; ++i)
34 |             Sleep(20);
35 |         __AUninstallHook();
36 |         break;
37 | 
38 |     case DLL_THREAD_ATTACH:
39 |         // attach to thread
40 |         break;
41 | 
42 |     case DLL_THREAD_DETACH:
43 |         // detach from thread
44 |         break;
45 |     }
46 |     return TRUE; // succesful
47 | }
48 | 


--------------------------------------------------------------------------------
/src/avz_logger.cpp:
--------------------------------------------------------------------------------
  1 | #include "libavz.h"
  2 | 
  3 | void AAbstractLogger::_BeforeScript() {
  4 |     _pattern = "#";
  5 |     _headerStyle = "[{wave}, {time}][{level}] ";
  6 | }
  7 | 
  8 | void AAbstractLogger::_Replace(std::string& content, std::string_view pattern, std::string_view replaceStr) {
  9 |     if (pattern.empty())
 10 |         return;
 11 |     size_t idx = 0;
 12 |     while (true) {
 13 |         idx = content.find(pattern, idx);
 14 |         if (idx == std::string::npos)
 15 |             break;
 16 |         content.replace(idx, pattern.size(), replaceStr);
 17 |         idx += replaceStr.size();
 18 |     }
 19 | }
 20 | 
 21 | std::string AAbstractLogger::_CreateHeader(ALogLevel level) {
 22 |     std::string headerFormat = _headerStyle;
 23 |     _Replace(headerFormat, _pattern + "wave", "{0}");
 24 |     _Replace(headerFormat, "{wave", "{0");
 25 |     _Replace(headerFormat, _pattern + "time", "{1}");
 26 |     _Replace(headerFormat, "{time", "{1");
 27 |     _Replace(headerFormat, _pattern + "level", "{2}");
 28 |     _Replace(headerFormat, "{level", "{2");
 29 |     _Replace(headerFormat, _pattern + "flag", "{3}");
 30 |     _Replace(headerFormat, "{flag", "{3");
 31 |     ATime now = ANowTime(false);
 32 |     int flag = AGetMainObject() ? AGetMainObject()->CompletedRounds() * 2 : 0;
 33 |     return std::vformat(headerFormat, std::make_format_args(now.wave, now.time, _levelStr[int(level)], flag));
 34 | }
 35 | 
 36 | void ALogger::_Output(ALogLevel level, std::string&& str) {
 37 |     if (_outFile.good()) {
 38 |         _outFile << AStrToWstr(str);
 39 |         _outFile.flush();
 40 |     }
 41 | }
 42 | 
 43 | void ALogger::_BeforeScript() {
 44 |     AAbstractLogger::_BeforeScript();
 45 |     if (!_outFile.is_open()) {
 46 |         _outFile.open(_fileName, std::ios::out | std::ios::app);
 47 |         if (!_outFile.good() && aLogger != this) {
 48 |             aLogger->Error("无法打开 #", _fileName);
 49 |             return;
 50 |         }
 51 |         _outFile.imbue(std::locale(""));
 52 |     }
 53 | }
 54 | 
 55 | void ALogger::_ExitFight() {
 56 |     _outFile.close();
 57 | }
 58 | 
 59 | // 清除文件中的所有内容
 60 | bool ALogger::Clear() {
 61 |     if (!_outFile.good())
 62 |         return false;
 63 |     _outFile.close();
 64 |     _outFile.open(_fileName, std::ios::out);
 65 |     if (!_outFile.good() && aLogger != this) {
 66 |         aLogger->Error("无法打开 {}", _fileName);
 67 |         return false;
 68 |     }
 69 |     _outFile.imbue(std::locale(""));
 70 |     return true;
 71 | }
 72 | 
 73 | void ALogger::_Output(ALogLevel level, std::string&& str) {
 74 |     SetConsoleTextAttribute(_handle, _color[int(level)]);
 75 |     std::wprintf(AStrToWstr(str).c_str());
 76 |     SetConsoleTextAttribute(_handle, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE);
 77 | }
 78 | 
 79 | ALogger::ALogger() {
 80 |     _textColors[0] = AArgb(0xff, 0xff, 0xff, 0xff); // white
 81 |     _textColors[1] = AArgb(0xff, 0, 0xff, 0);       // green
 82 |     _textColors[2] = AArgb(0xff, 0xff, 0xff, 0);    // yellow
 83 |     _textColors[3] = AArgb(0xff, 0xff, 0, 0);       // red
 84 |     _rectColors[0] = AArgb(0xaf, 0, 0, 0);
 85 |     _rectColors[1] = AArgb(0xaf, 0, 0, 0);
 86 |     _rectColors[2] = AArgb(0xaf, 0, 0, 0);
 87 |     _rectColors[3] = AArgb(0xaf, 0, 0, 0);
 88 |     _remainTime = 500; // 控制显示的持续时间
 89 |     _pixelDisplay = {10, 500};
 90 | }
 91 | 
 92 | void ALogger::_Output(ALogLevel level, std::string&& str) {
 93 |     int lineCnt = 0;
 94 |     for (auto c : str)
 95 |         lineCnt += (c == '\n');
 96 |     _curBottom += lineCnt * _painter.GetFontSize();
 97 |     int globalClock = AGetMainObject()->GlobalClock();
 98 |     _displayList.push_back({level, std::move(str), globalClock, lineCnt});
 99 | }
100 | 
101 | void ALogger::_ShowTick() {
102 |     int globalClock = AGetMainObject()->GlobalClock();
103 |     int x = _pixelDisplay.x;
104 |     int y = _pixelDisplay.y;
105 |     int eraseCnt = _displayList.size();
106 |     for (auto iter = _displayList.rbegin(); iter != _displayList.rend(); ++iter, --eraseCnt) {
107 |         if (iter->gameClock + _remainTime < globalClock) // 持续时间到,需要删除
108 |             break;
109 |         y -= _painter.GetFontSize() * iter->lineCnt;
110 |         _painter.SetTextColor(_textColors[int(iter->level)]);
111 |         _painter.SetRectColor(_rectColors[int(iter->level)]);
112 |         _painter.Draw(AText(iter->str, x, y + _curBottom));
113 |     }
114 |     for (int i = 0; i < eraseCnt; ++i)
115 |         _displayList.pop_front();
116 |     if (_curBottom != 0) {
117 |         _curBottom -= _transitSpeed;
118 |         _curBottom = std::clamp(_curBottom, 0, INT_MAX);
119 |     }
120 | }
121 | 
122 | void ALogger::_BeforeScript() {
123 |     AAbstractLogger::_BeforeScript();
124 |     _curBottom = 0;
125 |     _displayList.clear();
126 |     _tickRunner = std::make_shared([this] { _ShowTick(); }, ATickRunner::GLOBAL);
127 | }
128 | 
129 | void ALogger::_Output(ALogLevel level, std::string&& str) {
130 |     AMsgBox::Show(str);
131 | }
132 | 
133 | void ALogger::_BeforeScript() {
134 |     AAbstractLogger::_BeforeScript();
135 | }
136 | 
137 | bool ALogger::_isAllocateConsole = false;
138 | 
139 | ALogger::ALogger() {
140 |     if (!_isAllocateConsole && AllocConsole()) {
141 |         _isAllocateConsole = true;
142 |         SetConsoleTitle(TEXT("AConsole"));
143 |         freopen("CON", "w", stdout);
144 |         setlocale(LC_ALL, "chs");
145 |     }
146 |     _color[0] = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE; // white
147 |     _color[1] = FOREGROUND_GREEN;                                    // green
148 |     _color[2] = FOREGROUND_RED | FOREGROUND_GREEN;                   // yellow
149 |     _color[3] = FOREGROUND_RED;                                      // red
150 | 
151 |     // 完成后,无需使用 CloseHandle 释放此句柄
152 |     _handle = GetStdHandle(STD_OUTPUT_HANDLE);
153 | }
154 | 
155 | ALogger::~ALogger() {
156 |     if (_isAllocateConsole) {
157 |         fclose(stdout);
158 |         _isAllocateConsole = false;
159 |         FreeConsole();
160 |     }
161 | }
162 | 


--------------------------------------------------------------------------------
/src/avz_state_hook.cpp:
--------------------------------------------------------------------------------
 1 | #include "libavz.h"
 2 | 
 3 | #define __ADefineHookClassFuncs(HookName)                                                    \
 4 |     __APublic##HookName##Hook::__APublic##HookName##Hook(int runOrder)                       \
 5 |     {                                                                                        \
 6 |         __ARegisterInitOp([this] {                                                           \
 7 |             this->_isRunAll = false;                                                         \
 8 |             this->_isRun = false;                                                            \
 9 |         });                                                                                  \
10 |         _iter = _GetHookContainer().emplace(runOrder, this);                                 \
11 |     }                                                                                        \
12 |     __APublic##HookName##Hook::~__APublic##HookName##Hook()                                  \
13 |     {                                                                                        \
14 |         _GetHookContainer().erase(_iter);                                                    \
15 |     }                                                                                        \
16 |     void __APublic##HookName##Hook::Run##HookName()                                          \
17 |     {                                                                                        \
18 |         if (!_isRun) {                                                                       \
19 |             _##HookName();                                                                   \
20 |             _isRun = true;                                                                   \
21 |         }                                                                                    \
22 |     }                                                                                        \
23 |     void __APublic##HookName##Hook::Reset()                                                  \
24 |     {                                                                                        \
25 |         _isRunAll = false;                                                                   \
26 |         for (auto&& hook : _GetHookContainer()) {                                            \
27 |             hook.second->_isRun = false;                                                     \
28 |         }                                                                                    \
29 |     }                                                                                        \
30 |     void __APublic##HookName##Hook::RunAll()                                                 \
31 |     {                                                                                        \
32 |         if (!_isRunAll) {                                                                    \
33 |             for (auto&& hook : _GetHookContainer()) {                                        \
34 |                 hook.second->Run##HookName();                                                \
35 |             }                                                                                \
36 |             _isRunAll = true;                                                                \
37 |         }                                                                                    \
38 |     }                                                                                        \
39 |     __APublic##HookName##Hook::HookContainer& __APublic##HookName##Hook::_GetHookContainer() \
40 |     {                                                                                        \
41 |         static HookContainer _;                                                              \
42 |         return _;                                                                            \
43 |     }                                                                                        \
44 |     bool __APublic##HookName##Hook::_isRunAll = false
45 | 
46 | __ADefineHookClassFuncs(BeforeScript);
47 | __ADefineHookClassFuncs(AfterScript);
48 | __ADefineHookClassFuncs(EnterFight);
49 | __ADefineHookClassFuncs(ExitFight);
50 | __ADefineHookClassFuncs(AfterInject);
51 | __ADefineHookClassFuncs(BeforeTick);
52 | __ADefineHookClassFuncs(AfterTick);
53 | __ADefineHookClassFuncs(BeforeExit);
54 | 


--------------------------------------------------------------------------------
/src/avz_tick_runner.cpp:
--------------------------------------------------------------------------------
 1 | #include "libavz.h"
 2 | 
 3 | void __ATickManager::RunQueue() {
 4 |     for (auto&& pool : _priQue) {
 5 |         // 不用这种遍历如果中途新增了元素可能会访问非法内存
 6 |         for (std::size_t idx = 0; idx < pool.Size(); ++idx)
 7 |             if (pool.IsAlive(idx) && pool[idx].isRunning)
 8 |                 pool[idx].operation();
 9 |     }
10 | }
11 | 
12 | void __ATickManager::Remove(int priority, std::size_t idx) {
13 |     auto&& pool = _priQue[_PriToIdx(priority)];
14 |     if (!pool.Remove(idx)) {
15 |         aLogger->Error("无法移除 {}", _GetInfoStr(priority, idx));
16 |         return;
17 |     }
18 |     aLogger->Info("移除 {}", _GetInfoStr(priority, idx));
19 | }
20 | 
21 | void __ATickManager::_BeforeScript() {
22 |     if (_runMode == ATickRunner::AFTER_INJECT)
23 |         return; // 如果是注入之后的运行帧则什么都不做
24 | 
25 |     for (auto&& pool : _priQue)
26 |         pool.Clear();
27 | }
28 | 
29 | std::string __ATickManager::_GetInfoStr(int priority, std::size_t idx) {
30 |     std::string mode;
31 |     switch (_runMode) {
32 |     case ATickRunner::ONLY_FIGHT:
33 |         mode = "ONLY_FIGHT";
34 |         break;
35 |     case ATickRunner::GLOBAL:
36 |         mode = "GLOBAL";
37 |         break;
38 |     case ATickRunner::AFTER_INJECT:
39 |         mode = "AFTER_INJECT";
40 |         break;
41 |     case ATickRunner::PAINT:
42 |         mode = "PAINT";
43 |         break;
44 |     default:
45 |         mode = "UNKNOWN";
46 |     }
47 |     auto&& pool = _priQue[_PriToIdx(priority)];
48 |     return std::format("帧运行 : (模式: {}) (ID: {}) (索引: {}) (优先级: {}) (池容量: {})",
49 |         mode, pool.GetId(idx), idx, priority, pool.Size());
50 | }
51 | 
52 | ATickHandle& ATickHandle::operator=(const ATickHandle& rhs) {
53 |     _idx = rhs._idx;
54 |     _id = rhs._id;
55 |     _runMode = rhs._runMode;
56 |     _priority = rhs._priority;
57 |     _isStopped = rhs._isStopped;
58 |     return *this;
59 | }
60 | 
61 | bool ATickHandle::IsStopped() const noexcept {
62 |     if (_isStopped)
63 |         return true;
64 |     return !__aig.tickManagers[_runMode].IsAlive(_priority, _idx, _id);
65 | }
66 | 
67 | void ATickHandle::Pause() noexcept {
68 |     if (IsStopped())
69 |         return;
70 |     __aig.tickManagers[_runMode].At(_priority, _idx).isRunning = false;
71 | }
72 | 
73 | bool ATickHandle::IsPaused() const noexcept {
74 |     if (IsStopped())
75 |         return true;
76 |     return !__aig.tickManagers[_runMode].At(_priority, _idx).isRunning;
77 | }
78 | 
79 | void ATickHandle::GoOn() noexcept {
80 |     if (IsStopped()) {
81 |         aLogger->Error("帧运行已停止,无法继续运行");
82 |         return;
83 |     }
84 |     __aig.tickManagers[_runMode].At(_priority, _idx).isRunning = true;
85 | }
86 | 
87 | void ATickHandle::Stop() {
88 |     // 卸载脚本行为会触发 Stop,
89 |     // 此时不应该做任何事
90 |     if (__aScriptManager.willBeExit || IsStopped())
91 |         return;
92 |     __aig.tickManagers[_runMode].Remove(_priority, _idx);
93 |     _isStopped = true;
94 | }
95 | 


--------------------------------------------------------------------------------
/src/libavz.cpp:
--------------------------------------------------------------------------------
 1 | #include "libavz.h"
 2 | 
 3 | AIceFiller aIceFiller;
 4 | APlantFixer aPlantFixer;
 5 | ACobManager aCobManager;
 6 | AAliveFilter aAliveZombieFilter;
 7 | AAliveFilter aAlivePlantFilter;
 8 | AAliveFilter aAliveSeedFilter;
 9 | AAliveFilter aAliveItemFilter;
10 | AAliveFilter aAlivePlaceItemFilter;
11 | APainter aPainter;
12 | 


--------------------------------------------------------------------------------
/src/minhook/buffer.h:
--------------------------------------------------------------------------------
 1 | /*
 2 |  *  MinHook - The Minimalistic API Hooking Library for x64/x86
 3 |  *  Copyright (C) 2009-2017 Tsuda Kageyu.
 4 |  *  All rights reserved.
 5 |  *
 6 |  *  Redistribution and use in source and binary forms, with or without
 7 |  *  modification, are permitted provided that the following conditions
 8 |  *  are met:
 9 |  *
10 |  *   1. Redistributions of source code must retain the above copyright
11 |  *      notice, this list of conditions and the following disclaimer.
12 |  *   2. Redistributions in binary form must reproduce the above copyright
13 |  *      notice, this list of conditions and the following disclaimer in the
14 |  *      documentation and/or other materials provided with the distribution.
15 |  *
16 |  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 |  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 |  *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 |  *  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
20 |  *  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 |  *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 |  *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 |  *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 |  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 |  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 |  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |  */
28 | 
29 | #pragma once
30 | 
31 | // Size of each memory slot.
32 | #if defined(_M_X64) || defined(__x86_64__)
33 |     #define MEMORY_SLOT_SIZE 64
34 | #else
35 |     #define MEMORY_SLOT_SIZE 32
36 | #endif
37 | 
38 | VOID   InitializeBuffer(VOID);
39 | VOID   UninitializeBuffer(VOID);
40 | LPVOID AllocateBuffer(LPVOID pOrigin);
41 | VOID   FreeBuffer(LPVOID pBuffer);
42 | BOOL   IsExecutableAddress(LPVOID pAddress);
43 | 


--------------------------------------------------------------------------------
/src/minhook/hde32.h:
--------------------------------------------------------------------------------
  1 | /*
  2 |  * Hacker Disassembler Engine 32
  3 |  * Copyright (c) 2006-2009, Vyacheslav Patkov.
  4 |  * All rights reserved.
  5 |  *
  6 |  * hde32.h: C/C++ header file
  7 |  *
  8 |  */
  9 | 
 10 | #ifndef _HDE32_H_
 11 | #define _HDE32_H_
 12 | 
 13 | /* stdint.h - C99 standard header
 14 |  * http://en.wikipedia.org/wiki/stdint.h
 15 |  *
 16 |  * if your compiler doesn't contain "stdint.h" header (for
 17 |  * example, Microsoft Visual C++), you can download file:
 18 |  *   http://www.azillionmonkeys.com/qed/pstdint.h
 19 |  * and change next line to:
 20 |  *   #include "pstdint.h"
 21 |  */
 22 | #include "pstdint.h"
 23 | 
 24 | #define F_MODRM         0x00000001
 25 | #define F_SIB           0x00000002
 26 | #define F_IMM8          0x00000004
 27 | #define F_IMM16         0x00000008
 28 | #define F_IMM32         0x00000010
 29 | #define F_DISP8         0x00000020
 30 | #define F_DISP16        0x00000040
 31 | #define F_DISP32        0x00000080
 32 | #define F_RELATIVE      0x00000100
 33 | #define F_2IMM16        0x00000800
 34 | #define F_ERROR         0x00001000
 35 | #define F_ERROR_OPCODE  0x00002000
 36 | #define F_ERROR_LENGTH  0x00004000
 37 | #define F_ERROR_LOCK    0x00008000
 38 | #define F_ERROR_OPERAND 0x00010000
 39 | #define F_PREFIX_REPNZ  0x01000000
 40 | #define F_PREFIX_REPX   0x02000000
 41 | #define F_PREFIX_REP    0x03000000
 42 | #define F_PREFIX_66     0x04000000
 43 | #define F_PREFIX_67     0x08000000
 44 | #define F_PREFIX_LOCK   0x10000000
 45 | #define F_PREFIX_SEG    0x20000000
 46 | #define F_PREFIX_ANY    0x3f000000
 47 | 
 48 | #define PREFIX_SEGMENT_CS   0x2e
 49 | #define PREFIX_SEGMENT_SS   0x36
 50 | #define PREFIX_SEGMENT_DS   0x3e
 51 | #define PREFIX_SEGMENT_ES   0x26
 52 | #define PREFIX_SEGMENT_FS   0x64
 53 | #define PREFIX_SEGMENT_GS   0x65
 54 | #define PREFIX_LOCK         0xf0
 55 | #define PREFIX_REPNZ        0xf2
 56 | #define PREFIX_REPX         0xf3
 57 | #define PREFIX_OPERAND_SIZE 0x66
 58 | #define PREFIX_ADDRESS_SIZE 0x67
 59 | 
 60 | #pragma pack(push,1)
 61 | 
 62 | typedef struct {
 63 |     uint8_t len;
 64 |     uint8_t p_rep;
 65 |     uint8_t p_lock;
 66 |     uint8_t p_seg;
 67 |     uint8_t p_66;
 68 |     uint8_t p_67;
 69 |     uint8_t opcode;
 70 |     uint8_t opcode2;
 71 |     uint8_t modrm;
 72 |     uint8_t modrm_mod;
 73 |     uint8_t modrm_reg;
 74 |     uint8_t modrm_rm;
 75 |     uint8_t sib;
 76 |     uint8_t sib_scale;
 77 |     uint8_t sib_index;
 78 |     uint8_t sib_base;
 79 |     union {
 80 |         uint8_t imm8;
 81 |         uint16_t imm16;
 82 |         uint32_t imm32;
 83 |     } imm;
 84 |     union {
 85 |         uint8_t disp8;
 86 |         uint16_t disp16;
 87 |         uint32_t disp32;
 88 |     } disp;
 89 |     uint32_t flags;
 90 | } hde32s;
 91 | 
 92 | #pragma pack(pop)
 93 | 
 94 | #ifdef __cplusplus
 95 | extern "C" {
 96 | #endif
 97 | 
 98 | /* __cdecl */
 99 | unsigned int hde32_disasm(const void *code, hde32s *hs);
100 | 
101 | #ifdef __cplusplus
102 | }
103 | #endif
104 | 
105 | #endif /* _HDE32_H_ */
106 | 


--------------------------------------------------------------------------------
/src/minhook/pstdint.h:
--------------------------------------------------------------------------------
 1 | /*
 2 |  *  MinHook - The Minimalistic API Hooking Library for x64/x86
 3 |  *  Copyright (C) 2009-2017 Tsuda Kageyu. All rights reserved.
 4 |  *
 5 |  *  Redistribution and use in source and binary forms, with or without
 6 |  *  modification, are permitted provided that the following conditions
 7 |  *  are met:
 8 |  *
 9 |  *  1. Redistributions of source code must retain the above copyright
10 |  *     notice, this list of conditions and the following disclaimer.
11 |  *  2. Redistributions in binary form must reproduce the above copyright
12 |  *     notice, this list of conditions and the following disclaimer in the
13 |  *     documentation and/or other materials provided with the distribution.
14 |  *
15 |  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
16 |  *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 |  *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 |  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 |  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 |  *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 |  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 |  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 |  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 |  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |  */
26 | 
27 | #pragma once
28 | 
29 | #include 
30 | 
31 | // Integer types for HDE.
32 | typedef INT8   int8_t;
33 | typedef INT16  int16_t;
34 | typedef INT32  int32_t;
35 | typedef INT64  int64_t;
36 | typedef UINT8  uint8_t;
37 | typedef UINT16 uint16_t;
38 | typedef UINT32 uint32_t;
39 | typedef UINT64 uint64_t;
40 | 


--------------------------------------------------------------------------------
/src/minhook/table32.h:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * Hacker Disassembler Engine 32 C
 3 |  * Copyright (c) 2008-2009, Vyacheslav Patkov.
 4 |  * All rights reserved.
 5 |  *
 6 |  */
 7 | 
 8 | #define C_NONE    0x00
 9 | #define C_MODRM   0x01
10 | #define C_IMM8    0x02
11 | #define C_IMM16   0x04
12 | #define C_IMM_P66 0x10
13 | #define C_REL8    0x20
14 | #define C_REL32   0x40
15 | #define C_GROUP   0x80
16 | #define C_ERROR   0xff
17 | 
18 | #define PRE_ANY  0x00
19 | #define PRE_NONE 0x01
20 | #define PRE_F2   0x02
21 | #define PRE_F3   0x04
22 | #define PRE_66   0x08
23 | #define PRE_67   0x10
24 | #define PRE_LOCK 0x20
25 | #define PRE_SEG  0x40
26 | #define PRE_ALL  0xff
27 | 
28 | #define DELTA_OPCODES      0x4a
29 | #define DELTA_FPU_REG      0xf1
30 | #define DELTA_FPU_MODRM    0xf8
31 | #define DELTA_PREFIXES     0x130
32 | #define DELTA_OP_LOCK_OK   0x1a1
33 | #define DELTA_OP2_LOCK_OK  0x1b9
34 | #define DELTA_OP_ONLY_MEM  0x1cb
35 | #define DELTA_OP2_ONLY_MEM 0x1da
36 | 
37 | unsigned char hde32_table[] = {
38 |   0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,
39 |   0xa8,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0xaa,0xb2,0xaa,0x9f,0x9f,
40 |   0x9f,0x9f,0xb5,0xa3,0xa3,0xa4,0xaa,0xaa,0xba,0xaa,0x96,0xaa,0xa8,0xaa,0xc3,
41 |   0xc3,0x96,0x96,0xb7,0xae,0xd6,0xbd,0xa3,0xc5,0xa3,0xa3,0x9f,0xc3,0x9c,0xaa,
42 |   0xaa,0xac,0xaa,0xbf,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0x90,
43 |   0x82,0x7d,0x97,0x59,0x59,0x59,0x59,0x59,0x7f,0x59,0x59,0x60,0x7d,0x7f,0x7f,
44 |   0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x9a,0x88,0x7d,
45 |   0x59,0x50,0x50,0x50,0x50,0x59,0x59,0x59,0x59,0x61,0x94,0x61,0x9e,0x59,0x59,
46 |   0x85,0x59,0x92,0xa3,0x60,0x60,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,
47 |   0x59,0x59,0x9f,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xcc,0x01,0xbc,0x03,0xf0,
48 |   0x10,0x10,0x10,0x10,0x50,0x50,0x50,0x50,0x14,0x20,0x20,0x20,0x20,0x01,0x01,
49 |   0x01,0x01,0xc4,0x02,0x10,0x00,0x00,0x00,0x00,0x01,0x01,0xc0,0xc2,0x10,0x11,
50 |   0x02,0x03,0x11,0x03,0x03,0x04,0x00,0x00,0x14,0x00,0x02,0x00,0x00,0xc6,0xc8,
51 |   0x02,0x02,0x02,0x02,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xca,
52 |   0x01,0x01,0x01,0x00,0x06,0x00,0x04,0x00,0xc0,0xc2,0x01,0x01,0x03,0x01,0xff,
53 |   0xff,0x01,0x00,0x03,0xc4,0xc4,0xc6,0x03,0x01,0x01,0x01,0xff,0x03,0x03,0x03,
54 |   0xc8,0x40,0x00,0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,
55 |   0x00,0x00,0x00,0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,
56 |   0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
57 |   0x00,0xff,0xff,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
58 |   0x7f,0x00,0x00,0xff,0x4a,0x4a,0x4a,0x4a,0x4b,0x52,0x4a,0x4a,0x4a,0x4a,0x4f,
59 |   0x4c,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x55,0x45,0x40,0x4a,0x4a,0x4a,
60 |   0x45,0x59,0x4d,0x46,0x4a,0x5d,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,
61 |   0x4a,0x4a,0x4a,0x4a,0x4a,0x61,0x63,0x67,0x4e,0x4a,0x4a,0x6b,0x6d,0x4a,0x4a,
62 |   0x45,0x6d,0x4a,0x4a,0x44,0x45,0x4a,0x4a,0x00,0x00,0x00,0x02,0x0d,0x06,0x06,
63 |   0x06,0x06,0x0e,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x00,0x06,0x06,0x02,0x06,
64 |   0x00,0x0a,0x0a,0x07,0x07,0x06,0x02,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04,
65 |   0x04,0x04,0x00,0x00,0x00,0x0e,0x05,0x06,0x06,0x06,0x01,0x06,0x00,0x00,0x08,
66 |   0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,
67 |   0x86,0x00,0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,
68 |   0xf8,0xbb,0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,
69 |   0xc4,0xff,0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,
70 |   0x13,0x09,0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,
71 |   0xb2,0xff,0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,
72 |   0xe7,0x08,0x00,0xf0,0x02,0x00
73 | };
74 | 


--------------------------------------------------------------------------------
/src/minhook/trampoline.h:
--------------------------------------------------------------------------------
  1 | /*
  2 |  *  MinHook - The Minimalistic API Hooking Library for x64/x86
  3 |  *  Copyright (C) 2009-2017 Tsuda Kageyu.
  4 |  *  All rights reserved.
  5 |  *
  6 |  *  Redistribution and use in source and binary forms, with or without
  7 |  *  modification, are permitted provided that the following conditions
  8 |  *  are met:
  9 |  *
 10 |  *   1. Redistributions of source code must retain the above copyright
 11 |  *      notice, this list of conditions and the following disclaimer.
 12 |  *   2. Redistributions in binary form must reproduce the above copyright
 13 |  *      notice, this list of conditions and the following disclaimer in the
 14 |  *      documentation and/or other materials provided with the distribution.
 15 |  *
 16 |  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 17 |  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 18 |  *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 19 |  *  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 20 |  *  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 21 |  *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 22 |  *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 23 |  *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 24 |  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 25 |  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 26 |  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 27 |  */
 28 | 
 29 | #pragma once
 30 | 
 31 | #pragma pack(push, 1)
 32 | 
 33 | // Structs for writing x86/x64 instructions.
 34 | 
 35 | // 8-bit relative jump.
 36 | typedef struct _JMP_REL_SHORT
 37 | {
 38 |     UINT8  opcode;      // EB xx: JMP +2+xx
 39 |     UINT8  operand;
 40 | } JMP_REL_SHORT, *PJMP_REL_SHORT;
 41 | 
 42 | // 32-bit direct relative jump/call.
 43 | typedef struct _JMP_REL
 44 | {
 45 |     UINT8  opcode;      // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx
 46 |     UINT32 operand;     // Relative destination address
 47 | } JMP_REL, *PJMP_REL, CALL_REL;
 48 | 
 49 | // 64-bit indirect absolute jump.
 50 | typedef struct _JMP_ABS
 51 | {
 52 |     UINT8  opcode0;     // FF25 00000000: JMP [+6]
 53 |     UINT8  opcode1;
 54 |     UINT32 dummy;
 55 |     UINT64 address;     // Absolute destination address
 56 | } JMP_ABS, *PJMP_ABS;
 57 | 
 58 | // 64-bit indirect absolute call.
 59 | typedef struct _CALL_ABS
 60 | {
 61 |     UINT8  opcode0;     // FF15 00000002: CALL [+6]
 62 |     UINT8  opcode1;
 63 |     UINT32 dummy0;
 64 |     UINT8  dummy1;      // EB 08:         JMP +10
 65 |     UINT8  dummy2;
 66 |     UINT64 address;     // Absolute destination address
 67 | } CALL_ABS;
 68 | 
 69 | // 32-bit direct relative conditional jumps.
 70 | typedef struct _JCC_REL
 71 | {
 72 |     UINT8  opcode0;     // 0F8* xxxxxxxx: J** +6+xxxxxxxx
 73 |     UINT8  opcode1;
 74 |     UINT32 operand;     // Relative destination address
 75 | } JCC_REL;
 76 | 
 77 | // 64bit indirect absolute conditional jumps that x64 lacks.
 78 | typedef struct _JCC_ABS
 79 | {
 80 |     UINT8  opcode;      // 7* 0E:         J** +16
 81 |     UINT8  dummy0;
 82 |     UINT8  dummy1;      // FF25 00000000: JMP [+6]
 83 |     UINT8  dummy2;
 84 |     UINT32 dummy3;
 85 |     UINT64 address;     // Absolute destination address
 86 | } JCC_ABS;
 87 | 
 88 | #pragma pack(pop)
 89 | 
 90 | typedef struct _TRAMPOLINE
 91 | {
 92 |     LPVOID pTarget;         // [In] Address of the target function.
 93 |     LPVOID pDetour;         // [In] Address of the detour function.
 94 |     LPVOID pTrampoline;     // [In] Buffer address for the trampoline and relay function.
 95 | 
 96 | #if defined(_M_X64) || defined(__x86_64__)
 97 |     LPVOID pRelay;          // [Out] Address of the relay function.
 98 | #endif
 99 |     BOOL   patchAbove;      // [Out] Should use the hot patch area?
100 |     UINT   nIP;             // [Out] Number of the instruction boundaries.
101 |     UINT8  oldIPs[8];       // [Out] Instruction boundaries of the target function.
102 |     UINT8  newIPs[8];       // [Out] Instruction boundaries of the trampoline function.
103 | } TRAMPOLINE, *PTRAMPOLINE;
104 | 
105 | BOOL CreateTrampolineFunction(PTRAMPOLINE ct);
106 | 


--------------------------------------------------------------------------------
/tools/injector/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | aux_source_directory(src injector_srcs)
2 | add_executable(injector ${injector_srcs})
3 | target_link_options(injector PRIVATE -static)
4 | 


--------------------------------------------------------------------------------
/tools/injector/src/injector.h:
--------------------------------------------------------------------------------
 1 | #pragma once
 2 | 
 3 | #include 
 4 | #include 
 5 | #include 
 6 | #include 
 7 | 
 8 | class Process {
 9 | public:
10 |     template 
11 |     static T ReadMemory(HANDLE handle, Args... args) {
12 |         std::initializer_list lst = {static_cast(args)...};
13 |         uintptr_t buff = 0;
14 |         T result = T();
15 |         for (auto it = lst.begin(); it != lst.end(); ++it)
16 |             if (it != lst.end() - 1)
17 |                 ReadProcessMemory(handle, (const void*)(buff + *it), &buff, sizeof(buff), nullptr);
18 |             else
19 |                 ReadProcessMemory(handle, (const void*)(buff + *it), &result, sizeof(result), nullptr);
20 |         return result;
21 |     }
22 | 
23 | protected:
24 |     std::vector _hwnds;
25 |     HWND _selHwnd = nullptr;
26 |     DWORD _pid;
27 |     HANDLE _handle;
28 |     static BOOL CALLBACK _EnumWindowsProc(HWND hwnd, LPARAM lParam);
29 |     void _GetPvzHwnd();
30 |     void _SelectPvzHwnd();
31 |     static void _DealPvzWindow(HWND hwnd);
32 |     void _RemoveAllInjectedDll();
33 | 
34 | public:
35 |     Process();
36 |     ~Process();
37 | 
38 |     bool SelectWindow();
39 |     bool IsValid();
40 |     DWORD InjectDLL(PCWSTR);
41 |     DWORD EjectDLL(const std::string& dllName);
42 |     void ManageDLL();
43 |     void Write(uintptr_t addr, size_t len, uint8_t* data);
44 | };


--------------------------------------------------------------------------------
/tools/injector/src/main.cpp:
--------------------------------------------------------------------------------
 1 | #include "injector.h"
 2 | #include 
 3 | 
 4 | int main() {
 5 |     setlocale(LC_ALL, "");
 6 |     Process process;
 7 |     if (process.SelectWindow())
 8 |         process.ManageDLL();
 9 |     return 0;
10 | }
11 | 


--------------------------------------------------------------------------------
/tutorial/01start.md:
--------------------------------------------------------------------------------
 1 | 
 7 | 
 8 | # 起步
 9 | 
10 | ## 本教程只完全适配 `AvZ 2.7.1 2024_05_10` 版本,如果根据本教程编写的脚本出现语法错误,请先检查是否与该教程的版本相对应,如果确定自身没有任何问题,请及时联系我
11 | 
12 | 本框架 版本号按照以下方式查找
13 | ```C++
14 | // 本框架根目录->inc->libavz.h 
15 | #define __AVZ_VERSION__ 240205
16 | ```
17 | 
18 | 欢迎学习 AsmVsZombies 键控框架,AsmVsZombies 是由我和 [yuchenxi0_0](https://www.bilibili.com/video/BV1WJ41177a3) 两人合作完成,yuchenxi0_0 主要完成了反汇编注入工作,我主要完成了键控逻辑。我相信这套框架会带给脚本编写者焕然一新的使用体验。
19 | 
20 | 实际上这俩人是创始人,现在的框架所有功能实际上牵扯到的人太多了,我比较嫌麻烦,就不一一列举了
21 | 
22 | 
23 | ## 准备
24 | 
25 | **在使用 本框架 之前,我们需要进行一些准备,这些内容我放在了下面这个视频中,请一定耐心观看,不要错过任何一个细节。**
26 | 
27 | 接下来给出一些传送门
28 | 
29 | 视频教程:[bilibili](https://www.bilibili.com/video/BV1A7411V79A)
30 | 
31 | 开发安装包下载:
32 | 
33 | * [蓝奏云](https://wwu.lanzoue.com/b0188ujpc) 密码:buec
34 | 
35 | * [Gitee](https://gitee.com/vector-wlc/AsmVsZombies) 点击网页右边的发行版
36 | 
37 | 更新:
38 | 
39 | * 使用 VSCode 本框架 插件的命令 : 按下 Ctrl+Shift+P 组合键,然后再键入 本框架 : Update 再选择相应版本即可。
40 | 
41 | * 使用 AsmVsZombies 安装目录下的 `版本管理器.exe`, 双击运行,后续操作与前者相同。
42 | 
43 | * 使用代码存储库 [GitLab](https://gitlab.com/vector-wlc/AsmVsZombies/-/tree/master/release) / [GitHub](https://github.com/vector-wlc/AsmVsZombies/tree/master/release) / [Gitee](https://gitee.com/vector-wlc/AsmVsZombies/tree/master/release)  中的相应 \[版本\].zip 文件,下载下来,手动解压替换更新 (如果上面两个无法使用时,只能使用此方法)。
44 | 
45 | 
46 | QQ 群:[704655241](https://jq.qq.com/?_wv=1027&k=h6lNOpt0) ( 请先看完视频教程之后再加 )
47 | 
48 | 准备阶段完成之后,请继续结合视频教程完成自己的第一脚本 :经典十二炮。
49 | 
50 | 最后,希望 本框架 能够在键控方面帮助到您!
51 | 
52 | [目录](./0catalogue.md)
53 | 


--------------------------------------------------------------------------------
/tutorial/02hello_avz.md:
--------------------------------------------------------------------------------
 1 | 
 7 | # Hello AsmVsZombies
 8 | 
 9 | 粘贴以下代码可在 PvZ 的窗口中央显示一个维持 5s 的 `Hello AsmVsZombies`
10 | 
11 | ```C++
12 | #include   // 包含本框架的头文件
13 | 
14 | // 本框架的普通入口函数是 `void AScript()`
15 | void AScript()
16 | {
17 |     // 注意此条代码需要到选卡界面或者战斗界面才能看到运行效果
18 |     aPainter.Draw(AText("Hello AsmVsZombies", 400, 300), 500);
19 | }
20 | ```
21 | 
22 | 如果你的显示结果和下图一样,那么恭喜你已经安装好了本框架
23 | 
24 | ![结果显示图](./img/hello_avz.jpg)
25 | 
26 | 此外,本框架依然有协程版本的入口函数
27 | 
28 | ```C++
29 | #include  // 包含本框架的头文件
30 | 
31 | // 本框架的协程入口函数是 `ACoroutine ACoScript()`
32 | // 使用协程入口函数可以很方便的使用阻塞功能
33 | // 此功能后续会有展示
34 | ACoroutine ACoScript()
35 | {
36 |     // 注意此条代码需要到选卡界面或者战斗界面才能看到运行效果
37 |     aPainter.Draw(AText("Hello AsmVsZombies", 400, 300), 500);
38 |     co_return;
39 | }
40 | ```
41 | 
42 | 
43 | [目录](./0catalogue.md)


--------------------------------------------------------------------------------
/tutorial/03coding_style.md:
--------------------------------------------------------------------------------
 1 | 
 7 | 
 8 | # 编码风格
 9 | 
10 | **注意,本页教程看不懂是什么意思无所谓,因为需要一些 C++ 基础,但是用着用着自然而然就懂了**
11 | 
12 | 为了避免与其他库的标识符冲突,本框架所有的全局名称空间标识符都含有一个 `A`(类、函数、常量) 或者 `a` (对象、变量),例如
13 | 
14 | ``` C++
15 | 
16 | // C++ 使用双斜线作为注释,例如这行文字就是一个注释
17 | // 在这里不需要了解下面的语句是什么意思,只需要记住本框架的命名规范
18 | 
19 | // 函数为大驼峰命名法并加有前缀 A
20 | AShovel(3, 4); 
21 | ACard(AXRK_1, 3, 4);
22 | 
23 | // 类为大驼峰命名法并加有前缀 A
24 | ACobManager
25 | AIceFiller
26 | 
27 | // 变量或对象为小驼峰命名法并加有前缀 a
28 | aCobManger
29 | aIceFiller
30 | ```
31 | 
32 | [目录](./0catalogue.md)
33 | 


--------------------------------------------------------------------------------
/tutorial/04reload_mode.md:
--------------------------------------------------------------------------------
 1 | 
 7 | 
 8 | # 载入脚本模式
 9 | 
10 | 我们知道很多框架在录制视频时有一个比较烦人的问题,就是一旦录制失败了,得重新启动脚本再次进行录制,但是本框架提供了一个函数 `AReloadMode`,即设置载入脚本模式函数,来解决这个问题,使用示例如下:
11 | ```C++
12 | 
13 | // 脚本运行一次之后依然会在游戏在主界面或者战斗界面选卡界面重新载入
14 | // 注意这条语句经常用于挂机
15 | // 注意这条语句经常用于挂机
16 | // 注意这条语句经常用于挂机
17 | ASetReloadMode(AReloadMode::MAIN_UI_OR_FIGHT_UI);
18 | 
19 | // 脚本运行一次之后依然会在游戏在主界面重新载入
20 | ASetReloadMode(AReloadMode::MAIN_UI)
21 | 
22 | // 脚本运行一次之后不会再重新载入
23 | ASetReloadMode(AReloadMode::NONE) 
24 | 
25 | ```
26 | [目录](./0catalogue.md)
27 | 
28 | 


--------------------------------------------------------------------------------
/tutorial/06co_await.md:
--------------------------------------------------------------------------------
 1 | 
 7 | # 时间管理: 阻塞
 8 | 
 9 | 注意使用阻塞功能时必须使用 `ACoroutine ACoScript()` 形式的入口函数,否则会编译运行失败
10 | 在 PvZ 键控过程中,只有操作是不够的,还需要使操作在指定时间点运行,阻塞是实现这个需求的功能之一
11 | 
12 | 例:在 (1, -599) 也就是游戏一开始的时候将寒冰菇种在一行一列
13 | 
14 | ```C++
15 | co_await ATime(1, -599);
16 | ACard(AICE_SHROOM, 1, 1);
17 | ```
18 | 
19 | 那么我相信你可以写出在 (1, 0) 的时候发一门炮的代码了。 此时屏幕前的你:写啥写,我都不知道你这个框架如何发炮。 
20 | 啊这,看法宝:
21 | 
22 | ```C++
23 | co_await ATime(1, 0);
24 | aCobManager.Fire(1, 1); // 此行代码将会调用一门炮发送到一行一列
25 | ```
26 | 
27 | 乍一看上面的代码 `Fire(1, 1)` 能看懂,但是前面这个 `aCobManager.` 是什么东东,这里实际上不需要理解,
28 | 现在咱们只需要记住用炮的时候写一个这玩意就行了,理解不了还不会照葫芦画瓢吗。
29 | 
30 | 现在我们深入探讨一下上面几行代码,`co_await ATime` 这个函数到底做了什么,让操作可以在我们想要的时间点运行呢?
31 | 实际上很简单,顾名思义,`co_await ATime` 等待直到,其实就是阻塞,就是 `co_await ATime` 相当于一个拦路虎,
32 | 如果时间没到他的参数,他就会一直让脚本卡在这动不了,只有他的参数时间点到达了,他才会让程序继续运行下面的代码,
33 | OK,了解了这个玩意的工作原理之后,我们就得不得说一说他的优点和缺点了。
34 | 
35 | ### 优点
36 | 这个玩意的优点是啥呢,很简单,程序的可读性很高,看下面的代码
37 | 
38 | ```C++
39 | co_await ATime(1, -599);
40 | ACard(AICE_SHROOM, 1, 1);
41 | co_await ATime(1, 0);
42 | aCobManager.Fire(1, 1); // 此行代码将会调用一门炮发送到一行一列
43 | ```
44 | 
45 | 聪明的你一下就能看出来,奥这不就是前面两个代码的粘贴复制了一下吗,咳咳咳,咱们再解释一下,
46 | 在 (1, -599) 也就是游戏一开始的时候(第一波,僵尸刷新前 599cs)将寒冰菇种在一行一列,在 (1, 0) 的时候发一门炮 (好家伙就连解释也是粘贴复制的)。
47 | 嘶,好像这优点好像没啥啊?其实我是针对于后面的教程中即将介绍的 `AConnect` 来说的,你现在只需要暂时记住 `co_await ATime` 有这个优点就行。
48 | 
49 | ### 缺点
50 | 
51 | 要说到这玩意的缺点我可就不困了,首先他不允许时间点靠后运行的操作写在前面,比如
52 | 
53 | ```C++
54 | co_await ATime(1, 0);
55 | aCobManager.Fire(1, 1); // 此行代码将会调用一门炮发送到一行一列
56 | co_await ATime(1, -599);
57 | ACard(AICE_SHROOM, 1, 1);
58 | ```
59 | 
60 | 咱们详细的分析一下上面的代码,第一行代码就是将脚本的运行直接阻塞到了 (1, 0) 这个时间点,那么发炮这个操作
61 | 运行的时间点实际上是没什么毛病的,但是后面的用卡可就出大问题,人家本来是在(1, -599)这个时间点运行,
62 | 但是现在时间点已经到了 (1, 0),显然由于脚本的运行速度不可能超过光速,易证时间不可能倒流,所以这时候脚本就会报错:
63 | 现在时间已到 (1, 0),但是您要求的阻塞结束时间为 (1, -599),此阻塞无意义。
64 | 所以,这就是这个玩意的巨大缺点,就是书写顺序必须和时间先后顺序严格一致,不然直接 G。
65 | 所以在脚本的最外层使用阻塞不是一个明智的选择,但是他和接下来要说的连接联合使用,会迸发出相当大的威力
66 | 
67 | [目录](./0catalogue.md)


--------------------------------------------------------------------------------
/tutorial/07connect_time.md:
--------------------------------------------------------------------------------
  1 | 
  7 | # 时间管理: 连接
  8 | 
  9 | 首先说明一下,`AConnect`(连接)是本框架的核心功能之一,它可用于但不限于时间,
 10 | 考虑到大家一下接受不了那么多东西,这里就仅仅给大家介绍他关于时间方面的功能,
 11 | 
 12 | 还是接着阻塞往下说吧,比如我想实现 在 (1, -599) 也就是游戏一开始的时候(第一波,僵尸刷新前 599cs)将寒冰菇种在一行一列,在 (1, 0) 的时候发一门炮 这样的需求,那 `AConnect` 怎么写呢,答案就是:
 13 | 
 14 | ```C++
 15 | 
 16 | void FireCob(){
 17 |     aCobManager.Fire(1, 1); // 此行代码将会调用一门炮发送到一行一列
 18 | }
 19 | 
 20 | void UseCard(){
 21 |     ACard(AICE_SHROOM, 1, 1);
 22 | }
 23 | 
 24 | void AScript(){
 25 |     AConnect(ATime(1, 0), FireCob);
 26 |     AConnect(ATime(1, -599), UseCard);
 27 | }
 28 | 
 29 | ```
 30 | 
 31 | emmm, 不知道大家是不是能看懂,首先看 void AScript() 函数体的第一行, AConnect 的第一个参数是一个时间点,ATime(1, 0) 这个大家应该很好理解,
 32 | 那么第二个是啥玩意,实际上是个函数名,那 FireCob 这个函数干了什么? 就是调用了一下发炮函数,那实际上第一行的意思就很清楚了,
 33 | 就是建立了一条连接,这个连接有啥用呢,这个连接承诺当时间点到达 (1, 0) 的时候,调用 FireCob 这个函数,那 FireCob 这个函数
 34 | 又会调用  aCobManager.Fire(1, 1); 所以就实现了和阻塞类似的功能,第二行代码我就不解释了。我们可以很明确的看到,我没有按照时间点的先后顺序写
 35 | AConnect,那他会正确运行吗?我可以很明确的告诉你,能!他能!因为他是创建了一条连接,而不是阻塞了脚本的运行,这条连接创建是一瞬间的事,
 36 | 那你又有疑问了,那这条连接是啥时候创建的呢?**记住 void AScript() 这个函数是在脚本从游戏选择生存模式的那个界面点到选卡或者战斗界面的那一帧调用的**,因此这个连接也就在刚进入选卡或者战斗界面的时候被创建了,连接创建之后本框架宝宝就会时刻监视每一条连接,如果这个连接的触发条件满足,就会运行连接所对应的操作,所以**连接允许你随意书写代码顺序,不必像阻塞那样计算哪个时间点靠前还是靠后**
 37 | 
 38 | 那么此时你就会有疑问,能不能这么写:(这不就不用新写一个函数那么麻烦了吗?)
 39 | ```C++
 40 |     AConnect(ATime(1, 0), aCobManager.Fire(1, 1));
 41 | ```
 42 | 
 43 | **emmm, 我可以很明确的告诉你,不能,因为这并不是让 AConnect 去连接 aCobManager.Fire(1, 1) 这个操作,而是连接这个操作完成之后的返回值**,
 44 | 就像我正确代码中写的是 FireCob,而不是 FireCob(), 如果你无法理解这句话那就死记硬背,先记下来,先用着正确的方式,慢慢的你就理解了,没办法,学习新知识有的时候就是这样的。
 45 | 
 46 | 在这里我详细的解释一下,下面的解释的话你可能暂时理解不了,但是你总有一天会理解。
 47 | 
 48 | AConnect 的第二个参数接收一个可调用对象,这个可调用对象现在说的就比较抽象了,在 C++ 里面可调用对象大致分为三类
 49 | * 函数指针 (FireCob UseCard 就是函数指针)
 50 | * Lambda 表达式 (就是接下来即将介绍的)
 51 | * 重载了 operator() 类的对象 (实际上这是 Lambda 的原理,但是大家这里不需要掌握这个玩意)
 52 |  
 53 | 
 54 | 好了,如果你能看懂我上面的解释,就知道为啥填 `aCobManager.Fire(1, 1)` 不行了,因为 `aCobManager.Fire(1, 1)` 返回值为 int 类的对象(变量),很明显他不是一个可调用对象,所以不能这么写; 但是上面的话你如果看不懂,那就当我装了个逼,咱们接着说后面的部分。
 55 | 
 56 | 这时屏幕前的你就站出来了,说,真麻烦,合着每次用连接我还得写个函数啊,我还是回去用阻塞吧,但是,你先别急,连接还有一种较为简洁的调用方式,
 57 | 咱们这个框架用的 C++ 标准是 C++20, 这个标准非常牛逼,他支持 `Lambda 表达式`(实际上 C++11 标准就支持了),那么 Lambda 表达式是个啥玩意呢,
 58 | 咱们直接上代码吧。
 59 | 
 60 | 
 61 | ```C++
 62 | void AScript(){
 63 |     auto fireCob = []{
 64 |         aCobManager.Fire(1, 1); // 此行代码将会调用一门炮发送到一行一列
 65 |     };
 66 | 
 67 |     auto useCard = []{
 68 |         ACard(AICE_SHROOM, 1, 1);
 69 |     };
 70 |     AConnect(ATime(1, 0), fireCob);
 71 |     AConnect(ATime(1, -599), useCard);
 72 | }
 73 | 
 74 | ```
 75 | 
 76 | 这个代码就用到了 Lambda 表达式,你发现他好像是一个定义在 AScript 内部的函数,而且还是有个名字 fireCob 和 useCard,但是为了偷懒,我们可以不用这些名字,实际上 Lambda 还有一种说法就是匿名函数,其实就是没有名字的函数,咱们看下面的代码就更清晰了。
 77 | 
 78 | ```C++
 79 | void AScript(){
 80 |     AConnect(ATime(1, 0), []{
 81 |         aCobManager.Fire(1, 1); // 此行代码将会调用一门炮发送到一行一列
 82 |     });
 83 |     AConnect(ATime(1, -599), []{
 84 |         ACard(AICE_SHROOM, 1, 1);
 85 |     });
 86 | }
 87 | 
 88 | ```
 89 | 
 90 | 这个时候再看代码,你就发现确实没名字了,这样书写是正确的,可以这样理解,Lambda 有一个返回值,他需要被一个变量接收,之前的示例使用了 fireCob 和 useCard 进行了接收,但是现在,**直接使用了 AConnect 的形参进行了接收**,这样看着简洁了很多,实际上这才是咱们大多数情况下使用 AConnect 的姿势,如果你实在理解不了这个变化过程,我还是那句话,如果你无法理解这句话那就死记硬背,先记下来,先用着正确的方式,慢慢的你就理解了,没办法,学习新知识有的时候就是这样的。
 91 | 
 92 | 实际上你还是会说,这看着不如阻塞好看懂,是的,连接的可读性确实不如阻塞,但是你不用为了时间点的先后书写顺序发愁了啊,有得必有失嘛,慢慢适应,当你适应了 AConnect 的这种书写方式,你就会觉得 AConnect 真香,yyds !!!
 93 | 
 94 | 最后再说明一下,你可能疑惑,Lambda 表达式中的 [] 是啥意思,实际上这是一个捕获列表,在本框架下这个东西填一个 [] 也就是空捕获列表就行,少数情况下需要用 [=] 也就是按值的拷贝方式捕获外部变量,emmm 这怎么理解呢,看下面的代码
 95 | 
 96 | ```C++
 97 | void AScript(){
 98 |     int a = 1;
 99 | 
100 |     // 比如我想在这个 Lambda 表达式用这个 a
101 |     // 那需要写成 [=],而不是 []
102 |     AConnect(ATime(1, -599), [=]{
103 |         // 这时候就可以用 a 了
104 |         // 但是注意,这个 a 是外部的那个 a 的拷贝,而不是其本身
105 |         // 并且不能改变这个 a 的值
106 |         // 因为 lambda 默认使用 const 捕获
107 |         ACard(AICE_SHROOM, a, 1);
108 |        
109 |     });
110 | 
111 |     // 如果你需要使用外部变量,并且对其值进行修改
112 |     // 需要在 [=] 后面加上 () mutable
113 |     AConnect(ATime(1, -599), [=]() mutable{
114 |         // 这时候就可以用 a 了,并且可以对 a 进行修改
115 |         // 但是依然需要注意这个 a 是拷贝,不是引用
116 |         // 如果改写这个 a 是不会影响到外部的 a 的
117 |         // 那你这时候又会有疑问,我如果想改变外部的哪个 a 怎么办?
118 |         // 在本框架中如果发生了这种情况,我建议你使用全局变量,而不是引用捕获
119 |         // 如果你实在还想问引用捕获是什么,请利用你的浏览器吧
120 |         a = 2;
121 |         ACard(AICE_SHROOM, a, 1);
122 |        
123 |     });
124 | }
125 | ```
126 | 
127 | 
128 | [目录](./0catalogue.md)


--------------------------------------------------------------------------------
/tutorial/08cob_manager_1.md:
--------------------------------------------------------------------------------
 1 | 
 7 | 
 8 | # 炮管理类——初步
 9 | 
10 | 众所周知,炮序排布是一块难啃的瓜,如果解中存在着原地铲种炮、位移铲种炮、闪现铲种炮、超时空铲种炮,并且场地为天台时,排炮序可能让我们怀疑人生。因此我们需要一个强大的工具使我们从复杂的工作中解放出来,炮管理类便应运而生。吹逼结束。
11 | 
12 | 炮管理类是本框架中比较复杂的一个类,为了防止本文篇幅过长,这里只介绍其部分基础接口 。
13 | 
14 | 首先,炮管理类有一个预先创建的对象 aCobManager,其声明在头文件 libavz.h 中,声明如下:
15 | ```C++
16 | extern ACobManager aCobManager; 
17 | ```
18 | 
19 | 其中 ACobManager 为炮管理类的名称,aCobManager 是炮管理类实例化的对象,如果使用者在脚本中只使用一个炮列表,aCobManager 是足够的,下面介绍最基本的接口。
20 | 
21 | 
22 | 首先介绍炮列表是什么,炮列表实际上一个发射炮的列表
23 | ```C++
24 | aCobManager.SetList({{1, 2}, {2, 3}});
25 | ```
26 | 对于上条语句的意思是将一行二列,二行三列的炮存到 aCobManager 对象中,此时 aCobManager 就知道了自己在发射炮的时候发哪里的炮了。
27 | 
28 | ```C++
29 | aCobManager.AutoSetList();
30 | ```
31 | 上面这条语句是懒人专用,**他将场地上所有的炮自动全部载入这个对象的炮列表中**.
32 | 
33 | 接下来是发炮函数 `Fire`
34 | ```C++
35 | aCobManager.Fire(2, 9);
36 | aCobManager.Fire({{2, 9}, {5, 9}});
37 | ```
38 | 
39 | Fire 函数有两种调用形式,即为单发形式和多发形式,针对于示例中的炮列表,炮函数将会先把位于一行二列的炮发往二行九列,然后再把二行三列的炮发往二行九列,最后把一行二列的炮发往五行九列,注意对应顺序,SetList 告诉了 aCobManager 我能发射哪里的炮,Fire 则根据 SetList 中的位置书写顺序依次发射。
40 | 
41 | 接下来是等待炮恢复立即发炮函数 `RecoverFire`
42 | ```C++
43 | aCobManager.RecoverFire(2, 9);
44 | aCobManager.RecoverFire({{2, 9}, {5, 9}});
45 | ```
46 | 
47 | `RecoverFire` 函数会一直等待炮可用后再将炮立即发出去,其他方面和 `Fire` 函数相同。
48 | 
49 | 接下来是屋顶飞行时间调整函数 `RoofFire`
50 | ```C++
51 | aCobManager.RoofFire(2, 9);
52 | aCobManager.RoofFire({{2, 9}, {5, 9}});
53 | ```
54 | 
55 | 
56 | 我们知道,由于上届之风(游戏BUG)的影响,天台中位于不同列的炮其飞行时间也不同,这带来了非常复杂的时间调整问题,为了解决这一问题,`RoofFire` 统一将炮的飞行时间修正为了 387cs ,注意,**这里的修正并没有改变游戏规则,而是 本框架 根据炮所在的列数对其进行了适当的延后发射调整。**
57 | 
58 | 接下是最高发射权限函数 `RawFire` /  `RawRoofFire`
59 | ```C++
60 | aCobManager.RawFire(1, 1, 2, 9);
61 | aCobManager.RawFire({{1, 1, 2, 9}, {1, 2, 5, 9}});
62 | aCobManager.RawRoofFire(1, 1, 2, 9);
63 | aCobManager.RawRoofFire({{1, 1, 2, 9}, {1, 2, 5, 9}});
64 | ```
65 | 
66 | 对于第一个例子的解释是将位于一行一列的炮发往二行九列,其余的例子由上述解释很简单的推出是什么意思,这里要强调的是:RawFire 系列的函数是超级权限的函数,不论发射的炮是否位于 `SetList` 录入的位置,`RawFire` 都能发射,只要场地上目标位置确实存在炮,RawFire 函数都可以发射。 
67 | 
68 | **这里要再说一下,上述的所有发炮函数除了 Raw 系列的,所有的发炮函数如果成功发出了炮,会返回他发的炮在炮列表中的位置(从0开始),如果发射失败,则会返回一个 <0 的值**
69 | 
70 | 
71 | [目录](./0catalogue.md)
72 | 


--------------------------------------------------------------------------------
/tutorial/08time.md:
--------------------------------------------------------------------------------
 1 | # 时间体系
 2 | 
 3 | AvZ 中的时间用 (波次, 帧数) 的二元组表示,一帧对应游戏中的 0.01s,圈内也通常称为 1cs。**帧数可以超过本波波长,也可以为负数。**
 4 | 
 5 | 对于生存无尽,可用的波次范围为 0~21。其中第 0 波的基准时间为本次选卡开始(即 `PLANT!` 字样消失的时刻),第 1~20 波的基准时间为对应波僵尸生成,第 21 波的基准时间为本次选卡结束(即 `More zombies approaching!` 字样消失的时刻)。
 6 | AvZ 中最早可操作的时间为 (0, 1), 最晚可操作的时间为 (21, -1)。
 7 | 
 8 | 在游戏中,每一波的持续时长如下:
 9 | 
10 | - 0 波:固定 600
11 | - 1~8 波:每波 601~3100
12 | - 9 波:1346~5245
13 | - 10~18 波:每波 601~3100
14 | - 19 波:1346~5245
15 | - 20 波:500~5999
16 | 
17 | 在第 1~8, 11~18 波,本波僵尸总血量低于某个阈值后 200cs 会刷新下一波僵尸。这个检测最早在本波僵尸生成后 401cs 进行,因此波长至少为 601cs。此后每帧检测一次,直到波长达到一个上限(在 2500~3100 之间随机)后强制刷新下一波。
18 | 
19 | 在第 9, 19 波,本波除伴舞和魅惑僵尸以外的僵尸全部死亡后 945cs 会刷新下一波僵尸。这个检测最早在本波僵尸生成后 401cs 进行,因此波长至少为 1346cs。此后每帧检测一次,波长上限为 5245cs。
20 | 
21 | 在第 20 波,全场所有僵尸死亡后 499cs 会结束本轮选卡,5500cs 时强制触发结束选卡。结束选卡的标志为 `More zombies approaching!` 字样出现,这期间仍可操作。
22 | 
23 | AvZ 会自动读取内存数据以尽早确定每一波的基准时间。在不人为设置的情况下,每一波**确定基准时间的时刻**相对于该波的实际基准时间的偏移为:
24 | 
25 | - 0 波:1
26 | - 1 波:-599
27 | - 2~9 波:-200
28 | - 10 波:-945
29 | - 11~19 波:-200
30 | - 20 波:-945
31 | - 21 波:-499
32 | 
33 | 这意味着如果你将某个操作的时间设为 (2, -300),那么这个操作无法正常执行,因为在 (2, -300) 时,AvZ 还未确定第 2 波的基准时间。
34 | 
35 | 可以通过 `AAssumeWavelength`、`ASetWavelength` 以及 DSL 中的 `Trig`、`TrigAt` 等函数人为假定某些波的波长,从而可以使用更小的负数偏移。
36 | 例如,在调用 `AAssumeWavelength({{1, 601}, {2, 1200}})` 后,(3, -2000) 就变成了合法时间,对应 (0, 401)。
37 | 
38 | 可以通过 `ANowWave` 函数获取当前波。这个函数有两种行为:`ANowWave(false)` 返回当前的实际波次,`ANowWave(true)` 返回当前已确定基准时间的波次。
39 | 当前者返回 n 时,后者返回的值为 n 或 n+1。例如,若第 1 波波长为 601,则在 (1, 300) 时 `ANowWave(false) = ANowWave(true) = 1`,在 (1, 500) 时 `ANowWave(false) = 1, ANowWave(true) = 2`。
40 | 
41 | 可以通过 `ANowTime` 函数获取当前时间相对于某波的偏移量。例如,若第 1 波波长为 601,且未提前假定波长,则在 (1, 300) 时 `ANowTime(2) = INT_MIN`(当前尚未确定第 1 波波长),在 (1, 500) 时 `ANowTime(2) = -101`。
42 | 此外,`ANowTime(true)` 等效于 `ATime(ANowWave(true), ANowTime(ANowWave(true)))`,`ANowTime(false)` 等效于 `ATime(ANowWave(false), ANowTime(ANowWave(false)))`。
43 | 


--------------------------------------------------------------------------------
/tutorial/09first_tas_script.md:
--------------------------------------------------------------------------------
 1 | 
 7 | # 第一个键控脚本
 8 | 
 9 | 这里使用经典十二炮作为第一个键控脚本,经典十二炮可以说是最简单的键控脚本了,没有之一
10 | 
11 | **请强烈注意,我不是键控技术党,我是键控工具党,所以示例脚本的目的是教会大家如何使用本框架的接口,** 
12 | **脚本中的一些数值可能并不是科学的数值,科学的数值请询问当前植吧顶尖技术党或者查看相关的帖子和B站动态等,但由于本人早已不是键控技术党,**
13 | **而且没有精力和兴趣去学相关的键控技术知识。**
14 | 
15 | **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具**
16 | 
17 | **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具**
18 | 
19 | **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 
20 | 
21 | ## 阻塞版本
22 | 
23 | 见 [经典十二炮阻塞脚本](./scripts/jing_dian_12/jing_dian_12_co_await.cpp)
24 |  
25 | ## 连接版本
26 | 
27 | 见 [经典十二炮连接脚本](./scripts/jing_dian_12/jing_dian_12_connect.cpp)
28 | 
29 | [目录](./0catalogue.md)


--------------------------------------------------------------------------------
/tutorial/0catalogue.md:
--------------------------------------------------------------------------------
  1 |  
  7 | 
  8 | # 目录
  9 | 
 10 | ## 主库教程
 11 | 
 12 | 此部分教程是使用 AvZ 的基础,尽量学完此教程再看其他教程
 13 | 
 14 | 
15 | 点击左边的三角进行展开/收起 16 | 17 | * [起步](./01start.md) 18 | 19 | * [Hello AsmVsZombies](./02hello_avz.md) 20 | 21 | * [编码风格](./03coding_style.md) 22 | 23 | * [载入脚本模式(挂机函数)](./04reload_mode.md) 24 | 25 | * [卡片相关](./05card_shovel.md) 26 | 27 | * [时间管理: 阻塞](./06co_await.md) 28 | 29 | * [时间管理: 连接](./07connect_time.md) 30 | 31 | * [炮管理类——初步](./08cob_manager_1.md) 32 | 33 | * [时间体系](./08time.md) 34 | 35 | * [第一个键控脚本](./09first_tas_script.md) 36 | 37 | * [自动操作类](./10ice_filler.md) 38 | 39 | * [实用内存函数](./11memory_func.md) 40 | 41 | * [第二个键控脚本](./12second_tas_script.md) 42 | 43 | * [炮管理类——多炮列表](./13cob_manager_2.md) 44 | 45 | * [第三个键控脚本](./14third_tas_script.md) 46 | 47 | * [炮管理类——铲种](./15cob_manager_3.md) 48 | 49 | * [炮管理类——炮序模式](./16cob_manager_4.md) 50 | 51 | * [炮管理类——炮序排布](./17cob_manager_5.md) 52 | 53 | * [连接再探](./18connector.md) 54 | 55 | * [阻塞再探](./19coroutine.md) 56 | 57 | * [游戏控制函数](./20game_controllor.md) 58 | 59 | * [内存读取初步](./21read_memory_basic.md) 60 | 61 | * [帧运行](./22tick_runner.md) 62 | 63 | * [绘制类](./23painter.md) 64 | 65 | * [日志功能](./24logger.md) 66 | 67 | * [状态钩](./25state_hook.md) 68 | 69 | * [内存读取](./26read_memory.md) 70 | 71 | * [回放](./27replay.md) 72 | 73 | * [(附加) 两仪键控](./28liang_yi.md) 74 | 75 |
76 | 77 | 78 | ## DSL 教程 79 | 80 | 组件作者: [alumkal(github)](https://github.com/alumkal) 81 | 82 |
83 | 点击左边的三角进行展开/收起 84 | 85 | 正文: 86 | 87 | * [1 时间轴是什么](./dsl/1%20时间轴是什么.md) 88 | 89 | * [2 构建时间轴](./dsl/2%20构建时间轴.md) 90 | 91 | * [3 连接时间轴](./dsl/3%20连接时间轴.md) 92 | 93 | * [4 脚本示例:[FE] 经典四炮.md](./dsl/4%20脚本示例:[FE]%20经典四炮.md) 94 | 95 | 附录: 96 | 97 | * [A 关于 `shorthand.h`](./dsl/A%20关于%20shorthand.h.md) 98 | 99 |
100 | -------------------------------------------------------------------------------- /tutorial/10ice_filler.md: -------------------------------------------------------------------------------- 1 | 7 | # 自动操作类 8 | 9 | # 存冰类 10 | 11 | 接下来介绍一个简单而又重要的类:存冰类 12 | 13 | 在 本框架 中,存冰和使用冰的操作被封装成了类提供给大家使用,熟悉 C++ 的大佬鼠肯定知道,如果想使用类中的函数(这里指非静态函数),不可避免的是使用类创建一个对象,然后使用这个对象调用成员函数,但是现在不必这么做,因为 本框架 中提前为大家准备好了存冰类的对象 aIceFiller,其声明在 libavz.h 头文件下 14 | ```C++ 15 | extern AIceFiller aIceFiller; 16 | ``` 17 | 18 | 在这条语句中 AIceFiller 就是存冰类,aIceFiller 即为其创建(实例化)的对象,接下来说明如何使用 aIceFiller 这个对象完成存冰和使用冰的功能。 19 | 20 | ```C++ 21 | // 在一行二列,一行三列进行存冰 22 | // 注意是优先存 一行二列 再存 一行三列 23 | // 不要频繁调用此接口,性能消耗大 24 | aIceFiller.Start({{1, 2}, {1, 3}}); 25 | 26 | // 使用存冰列表中的冰 27 | // 注意 coffee 函数将会倒序使用存冰列表中的冰,例如如果使用示例 Start 中的列表,那么 Coffee 函数将会优先使用一行三列的存冰。 28 | aIceFiller.Coffee(); 29 | 30 | // 停止存冰 31 | // 完全停止这个对象的运行,如果长时间不再使用此对象需要调用此接口 32 | // 再次激活对象的运行需要调用 Start 33 | aIceFiller.Stop(); 34 | 35 | // 暂停存冰 36 | // 对象以一种低耗状态运行,如果短时间不使用此对象需要调用此接口 37 | // 再次激活对象的运行需要调用 goOn 38 | aIceFiller.Pause(); 39 | 40 | // 继续存冰 41 | aIceFiller.GoOn(); 42 | 43 | // 重置存冰列表为三行三列,三行四列 44 | aIceFiller.SetList({{3, 3}, {3, 4}}); 45 | 46 | // 只使用原版冰 47 | aIceFiller.SetIceSeedList({ICE_SHROOM}) 48 | 49 | // 优先使用模仿冰,再使用原版冰 50 | aIceFiller.SetIceSeedList({M_ICE_SHROOM, ICE_SHROOM}) 51 | ``` 52 | 53 | # 修补植物类 54 | 55 | 有时候在游戏中我们需要修补植物,本框架将这一功能封装为了 `APlantFixer` 类供大家使用。 56 | 57 | ```C++ 58 | extern APlantFixer aPlantFixer; 59 | ``` 60 | 61 | 在这条语句中,APlantFixer 是修补植物类,aPlantFixer 是其创建的对象,这条语句仍在头文件 libavz.h 中,接下来介绍修补植物类的接口函数。 62 | 63 | ## 接口简介 64 | 65 | ```C++ 66 | // 位置被重置为{2,3},{3,4} 67 | aPlantFixer.SetList({{2, 3}, {3, 4}}); 68 | 69 | // 自动得到修补的位置列表 70 | aPlantFixer.AutoSetList(); 71 | 72 | // 修补全场的高坚果 73 | aPlantFixer.Start(GJG_23) 74 | 75 | // 修补位置为{1,3},{2,3}位置的南瓜头 76 | aPlantFixer.Start(NGT_30, {{1, 3}, {2, 3}}) 77 | 78 | // 修补位置为{1,3},{2,3}位置的坚果,血量降至 300 开始修补 79 | aPlantFixer.Start(JG_3, {{1, 3}, {2, 3}}, 300) 80 | 81 | // 将修补触发血量改为200 82 | aPlantFixer.SetHp(200) 83 | 84 | // 使用咖啡豆 85 | aPlantFixer.SetIsUseCoffee(true) 86 | 87 | // 不使用咖啡豆 88 | aPlantFixer.SetIsUseCoffee(false) 89 | 90 | // Pause GoOn Stop 这三个接口与存冰类的含义相同,在此不再赘述 91 | 92 | ``` 93 | 94 | ## 同时修补多种植物 95 | 96 | 在一局游戏中,我们可能需要修补多种植物,但是需要注意的是,一个修补植物对象只能修补一种植物,那如何解决这个问题呢,其实非常简单,我们只需要使用修补植物类创建多个修补植物对象即可。 97 | ```C++ 98 | APlantFixer nutFixer; 99 | APlantFixer pumpkinFixer; 100 | 101 | void AScript(){ 102 | 103 | } 104 | ``` 105 | 注意,为了方便使用,需要将对象创建在 `AScript` 函数外部,像这样创建两个对象,用一个对象修补坚果,用另一个对象修补南瓜。 106 | 107 | ```C++ 108 | APlantFixer nutFixer; 109 | APlantFixer pumpkinFixer; 110 | void AScript(){ 111 | nutFixer.Start(JG_3); 112 | nutFixer.SetHp(4000 / 3 * 2); // 这里注意一定要在 Start 函数之后再调用 SetHp, 不然效果会被 Start 函数的默认值覆盖 113 | pumpkinFixer.Start(NGT_30); 114 | pumpkinFixer.SetHp(4000 / 3 * 2); 115 | } 116 | ``` 117 | 这样我们就可以同时修补坚果和南瓜了,到这里大家可能有个疑惑,为什么一个对象不能同时开两个 `Start` 呢?原因很简单,因为如果这个对象再调用 `Stop` 函数时,他自己会不知道要停止哪个修补任务。 118 | 119 | 总结:我们可以使用 `Start` `Pause` `GoOn` `Stop` 控制语句来达到精准控制修补植物对象修补状态的目的,同时可以使用一系列的属性设置函数来控制修补植物对象的修补类型和修补位置等属性,当一局游戏中需要修补多种植物时,需要创建多个修补植物对象。 120 | 121 | [目录](./0catalogue.md) 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /tutorial/11memory_func.md: -------------------------------------------------------------------------------- 1 | 7 | # 实用内存函数 8 | 9 | ## 女仆秘籍 10 | ```C++ 11 | 12 | // 召唤舞伴 13 | // 舞王不前进且每帧尝试召唤舞伴 14 | AMaidCheats::CallPartner(); 15 | // 跳舞 16 | // 舞王不前进且不会召唤舞伴 17 | AMaidCheats::Dancing(); 18 | 19 | // 保持前进 20 | // 舞王一直前进 21 | AMaidCheats::Move(); 22 | 23 | // 停止女仆秘籍 24 | // 恢复游戏原样 25 | AMaidCheats::Stop() 26 | 27 | ``` 28 | 29 | ## 设定植物精准生效函数 30 | 31 | ```C++ 32 | // 卡片精准生效 33 | // *** 注意此函数不允许大幅度修正生效时间,如果修改的时间与真实值超过 2cs,则会报错 34 | // *** 使用示例: 35 | // ASetPlantActiveTime(AICE_SHROOM, 298) --------- 修正寒冰菇生效时间点至当前时刻的 298cs 后 36 | void ASetPlantActiveTime(APlantType plantType, int delayTime, const ARunOrder& runOrder = ARunOrder(0)); 37 | 38 | // 设置冰卡精准生效 39 | // *** 注意巨人的投掷时间为 106 211 40 | // *** 注意此函数不允许大幅度修正生效时间,如果修改的时间与真实值超过 2cs,则会报错 41 | // 本框架1 中的 Ice3 由于作者比较脑瘫, 整错了一秒, 这里进行修正 42 | // *** 使用示例: 43 | // AIce3(298) --------- 修正寒冰菇生效时间点至当前时刻的 298cs 后 44 | // 此函数用的较多,所以就特地设了一个接口,实际上其内部调用的 ASetPlantActiveTime 45 | inline void AIce3(int delayTime, const ARunOrder& runOrder = ARunOrder(0)) 46 | { 47 | ASetPlantActiveTime(AICE_SHROOM, delayTime, runOrder); 48 | } 49 | ``` 50 | 51 | ## 设定游戏速度函数 52 | 53 | ```C++ 54 | // 设置游戏倍速 55 | // *** 注意:倍速设置的范围为 [0.05, 10] 56 | // *** 使用示例 57 | // ASetGameSpeed(5) ---- 将游戏速度设置为 5 倍速 58 | // ASetGameSpeed(0.1) --- 将游戏速度设置为 0.1 倍速 59 | void ASetGameSpeed(float x); 60 | ``` 61 | 62 | ## 设定僵尸出怪类型函数 63 | 64 | ```C++ 65 | // 设置出怪 66 | // 参数命名规则:与英文原版图鉴名称一致 67 | // *** 使用示例: 68 | // ASetZombies({ 69 | // APOLE_VAULTING_ZOMBIE, // 撑杆 70 | // ABUCKETHEAD_ZOMBIE, // 铁桶 71 | // AZOMBONI, // 冰车 72 | // AJACK_IN_THE_BOX_ZOMBIE, // 小丑 73 | // ABALLOON_ZOMBIE, // 气球 74 | // ALADDER_ZOMBIE, // 梯子 75 | // ACATAPULT_ZOMBIE, // 投篮 76 | // AGARGANTUAR, // 巨人 77 | // AGIGA_GARGANTUAR, // 红眼巨人 78 | // APOGO_ZOMBIE, // 跳跳 79 | // }); 80 | // 设置出怪类型为:撑杆 铁桶 冰车 小丑 气球 扶梯 投篮 白眼 红眼 跳跳 81 | // 82 | // ASetZombies({ 83 | // APOLE_VAULTING_ZOMBIE, // 撑杆 84 | // ABUCKETHEAD_ZOMBIE, // 铁桶 85 | // AZOMBONI, // 冰车 86 | // AJACK_IN_THE_BOX_ZOMBIE, // 小丑 87 | // ABALLOON_ZOMBIE, // 气球 88 | // ALADDER_ZOMBIE, // 梯子 89 | // ACATAPULT_ZOMBIE, // 投篮 90 | // AGARGANTUAR, // 巨人 91 | // AGIGA_GARGANTUAR, // 红眼巨人 92 | // APOGO_ZOMBIE, // 跳跳 93 | // }, ASetZombieMode::INTERNAL); 94 | // 设置出怪类型为:撑杆 铁桶 冰车 小丑 气球 扶梯 投篮 白眼 红眼 跳跳 95 | // 并设置为自然出怪 96 | // 97 | // ASetZombies({ 98 | // ABUCKETHEAD_ZOMBIE, 99 | // AZOMBONI, 100 | // AZOMBONI, 101 | // }); 102 | // 设置出怪类型为:铁桶 冰车 并且两种僵尸的比例为 1:2 103 | void ASetZombies(const std::vector& zombie_type); 104 | 105 | // 参数命名规则:与英文原版图鉴名称一致 106 | // *** 使用示例: 107 | // ASetWaveZombies(1, { 108 | // APOLE_VAULTING_ZOMBIE, // 撑杆 109 | // ABUCKETHEAD_ZOMBIE, // 铁桶 110 | // AZOMBONI, // 冰车 111 | // AJACK_IN_THE_BOX_ZOMBIE, // 小丑 112 | // ABALLOON_ZOMBIE, // 气球 113 | // ALADDER_ZOMBIE, // 梯子 114 | // ACATAPULT_ZOMBIE, // 投篮 115 | // AGARGANTUAR, // 巨人 116 | // AGIGA_GARGANTUAR, // 红眼巨人 117 | // APOGO_ZOMBIE, // 跳跳 118 | // }); 119 | // 设置第一波出怪类型为:撑杆 铁桶 冰车 小丑 气球 扶梯 投篮 白眼 红眼 跳跳 120 | // 121 | // ASetWaveZombies(1, { 122 | // ABUCKETHEAD_ZOMBIE, 123 | // AZOMBONI, 124 | // AZOMBONI, 125 | // }); 126 | // 设置第一波出怪类型为:铁桶 冰车 并且两种僵尸的比例为 1:2 127 | void ASetWaveZombies(int wave, const std::vector& zombie_type); 128 | ``` 129 | 130 | ## 波长相关函数 131 | 132 | ```C++ 133 | // 设定特定波的波长 134 | // 波长的设定范围为 601 - 2510 135 | // *** 使用示例: 136 | // ASetWavelength({ATime(1, 601), ATime(4, 1000)}) ----- 将第一波的波长设置为 601,将第四波的波长设置为 1000 137 | void ASetWavelength(const std::vector& lst, const ARunOrder& runOrder = ARunOrder(0)); 138 | 139 | // 假定特定波的波长 140 | // *** 注意: wave 9 19 20 无法假定波长 141 | // 波长的假定范围为 [601, 2510] 142 | // 本函数与 ASetWavelength 区别在于, 本函数不会对内存进行修改 143 | // 只是可以让时间点的书写范围小于 -200, 如果真实的波长与假定的波长不一致, 则会报错 144 | // *** 使用示例: 145 | // AAssumeWavelength({ATime(1, 601), ATime(4, 1000)}) ----- 将第一波的波长假定为 601,将第四波的波长假定为 1000 146 | void AAssumeWavelength(const std::vector& lst, const ARunOrder& runOrder = ARunOrder(0)); 147 | ``` 148 | 149 | [目录](./0catalogue.md) 150 | 151 | ## 附录 152 | 153 | ```C++ 154 | 155 | enum AZombieType { 156 | AZOMBIE = 0, // 普僵 157 | AFLAG_ZOMBIE, // 旗帜 158 | ACONEHEAD_ZOMBIE, // 路障 159 | APOLE_VAULTING_ZOMBIE, // 撑杆 160 | ABUCKETHEAD_ZOMBIE, // 铁桶 161 | ANEWSPAPER_ZOMBIE, // 读报 162 | ASCREEN_DOOR_ZOMBIE, // 铁门 163 | AFOOTBALL_ZOMBIE, // 橄榄 164 | ADANCING_ZOMBIE, // 舞王 165 | ABACKUP_DANCER, // 伴舞 166 | ADUCKY_TUBE_ZOMBIE, // 鸭子 167 | ASNORKEL_ZOMBIE, // 潜水 168 | AZOMBONI, // 冰车 169 | AZOMBIE_BOBSLED_TEAM, // 雪橇 170 | ADOLPHIN_RIDER_ZOMBIE, // 海豚 171 | AJACK_IN_THE_BOX_ZOMBIE, // 小丑 172 | ABALLOON_ZOMBIE, // 气球 173 | ADIGGER_ZOMBIE, // 矿工 174 | APOGO_ZOMBIE, // 跳跳 175 | AZOMBIE_YETI, // 雪人 176 | ABUNGEE_ZOMBIE, // 蹦极 177 | ALADDER_ZOMBIE, // 扶梯 178 | ACATAPULT_ZOMBIE, // 投篮 179 | AGARGANTUAR, // 白眼 180 | AIMP, // 小鬼 181 | ADR_ZOMBOSS, // 僵博 182 | AGIGA_GARGANTUAR = 32 // 红眼 183 | }; 184 | 185 | constexpr AZombieType APJ_0 = AZOMBIE; // 普僵 186 | constexpr AZombieType AQZ_1 = AFLAG_ZOMBIE; // 旗帜 187 | constexpr AZombieType ALZ_2 = ACONEHEAD_ZOMBIE; // 路障 188 | constexpr AZombieType ACG_3 = APOLE_VAULTING_ZOMBIE; // 撑杆 189 | constexpr AZombieType ATT_4 = ABUCKETHEAD_ZOMBIE; // 铁桶 190 | constexpr AZombieType ADB_5 = ANEWSPAPER_ZOMBIE; // 读报 191 | constexpr AZombieType ATM_6 = ASCREEN_DOOR_ZOMBIE; // 铁门 192 | constexpr AZombieType AGL_7 = AFOOTBALL_ZOMBIE; // 橄榄 193 | constexpr AZombieType AWW_8 = ADANCING_ZOMBIE; // 舞王 194 | constexpr AZombieType ABW_9 = ABACKUP_DANCER; // 伴舞 195 | constexpr AZombieType AYZ_10 = ADUCKY_TUBE_ZOMBIE; // 鸭子 196 | constexpr AZombieType AQS_11 = ASNORKEL_ZOMBIE; // 潜水 197 | constexpr AZombieType ABC_12 = AZOMBONI; // 冰车 198 | constexpr AZombieType AXQ_13 = AZOMBIE_BOBSLED_TEAM; // 雪橇 199 | constexpr AZombieType AHT_14 = ADOLPHIN_RIDER_ZOMBIE; // 海豚 200 | constexpr AZombieType AXC_15 = AJACK_IN_THE_BOX_ZOMBIE; // 小丑 201 | constexpr AZombieType AQQ_16 = ABALLOON_ZOMBIE; // 气球 202 | constexpr AZombieType AKG_17 = ADIGGER_ZOMBIE; // 矿工 203 | constexpr AZombieType ATT_18 = APOGO_ZOMBIE; // 跳跳 204 | constexpr AZombieType AXR_19 = AZOMBIE_YETI; // 雪人 205 | constexpr AZombieType ABJ_20 = ABUNGEE_ZOMBIE; // 蹦极 206 | constexpr AZombieType AFT_21 = ALADDER_ZOMBIE; // 扶梯 207 | constexpr AZombieType ATL_22 = ACATAPULT_ZOMBIE; // 投篮 208 | constexpr AZombieType ABY_23 = AGARGANTUAR; // 白眼 209 | constexpr AZombieType AXG_24 = AIMP; // 小鬼 210 | constexpr AZombieType AJB_25 = ADR_ZOMBOSS; // 僵博 211 | constexpr AZombieType AHY_32 = AGIGA_GARGANTUAR; // 红眼 212 | ``` -------------------------------------------------------------------------------- /tutorial/12second_tas_script.md: -------------------------------------------------------------------------------- 1 | 7 | # 第二个键控脚本 8 | 9 | 这里使用经典四炮作为第二个键控脚本,经典四炮可以将我们刚才学习的那几个接口进行运用,这里不再提供阻塞版本 10 | 11 | **请强烈注意,我不是键控技术党,我是键控工具党,所以示例脚本的目的是教会大家如何使用本框架的接口,** 12 | **脚本中的一些数值并不是科学的数值,科学的数值请询问当前植吧顶尖技术党或者查看相关的帖子和B站动态等,但由于本人早已不是键控技术党,** 13 | **而且没有精力和兴趣去学相关的键控技术知识。** 14 | 15 | **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 16 | 17 | **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 18 | 19 | **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 20 | 21 | 见 [经典四炮阻塞脚本](./scripts/jing_dian_4/jing_dian_4.cpp) 22 | 23 | [目录](./0catalogue.md) -------------------------------------------------------------------------------- /tutorial/13cob_manager_2.md: -------------------------------------------------------------------------------- 1 | 7 | # 炮管理类——多炮列表 8 | 9 | 上一篇文章中我们知道了炮管理类中最基本的接口,这篇文章我们将讨论如何使用炮管理类创建多个炮列表。 10 | 首先我们需要知道为什么需要多炮列表,是因为在处理天台这样的场景时,由于 PvZ 的一个 BUG(上界之风), 11 | 导致不同列炮的飞行时间和落点偏移都不相同,如果将这些炮都放在一个炮列表中使用,那调炮序就会把人调麻了, 12 | 所以多炮列表应运而生 13 | 14 | 创建多个炮列表语法: 15 | 16 | ```C++ 17 | ACobManager windCob; // 风炮 18 | ACobManager groudCob; // 平地炮 19 | 20 | void AScript(){ 21 | 22 | } 23 | ``` 24 | 25 | **请注意在创建多个炮列表时,必须将炮列表定义为全局对象,即定义在函数 `void AScript()` 外部**,这点十分重要,如果定义在 `AScript` 函数内部,会为以后的使用带来不必要的麻烦。下面我们结合具体的例子说明多炮列表的意义。 26 | 27 | RE 十炮: 28 | 29 | ![RE 十炮](./img/RE10.jpg) 30 | 31 | 对于此阵,由于风炮(位于一列的炮)和平地炮(位于六列的炮)的落点偏移不同驱使着我们必须将平地炮发往四行,将风炮发往二行,否则会导致漏炸。所以此时如果我们使用一个炮列表则必须刻意的对炮的使用顺序进行设计,但是多炮列表就解决了这个麻烦,接下来我们看多炮列表是如何做到的。 32 | 33 | ```C++ 34 | windCob.SetList({{ 35 | {1, 1}, 36 | {2, 1}, 37 | {3, 1}, 38 | {4, 1}, 39 | {5, 1}, 40 | }}); 41 | // 风炮 42 | groudCob.SetList({{ 43 | {1, 6}, 44 | {2, 6}, 45 | {3, 6}, 46 | {4, 6}, 47 | {5, 6}, 48 | }}); 49 | ``` 50 | // 风炮 51 | windCob // 平地炮此时储存着风炮所在的位置,groudCob 储存着平地炮所在的位置,由于是天台,这里我们使用 `RoofFire` 函数,此时只要使用对应的炮操作对象调用 `RoofFire` 函数就可以使用相应位置的炮。 52 | ```C++ 53 | windCob.RoofFire(2, 9); 54 | groudCob.RoofFire(4, 9); 55 | ``` 56 | 57 | 这样我们就保证了发射的一对炮里必是一个来自平地炮一个来自风炮的,并且平地炮必射向四行九列,风炮必射向二行九列,这样就不用刻意设计炮位置的书写顺序了。 58 | 59 | 60 | [目录](./0catalogue.md) 61 | 62 | -------------------------------------------------------------------------------- /tutorial/14third_tas_script.md: -------------------------------------------------------------------------------- 1 | 7 | # 第三个键控脚本 8 | 9 | 这里使用天台十炮作为第三个键控脚本,天台十炮可以让我们体会到多炮列表的作用 10 | 11 | **请强烈注意,我不是键控技术党,我是键控工具党,所以示例脚本的目的是教会大家如何使用本框架的接口,** 12 | **脚本中的一些数值并不是科学的数值,科学的数值请询问当前植吧顶尖技术党或者查看相关的帖子和B站动态等,但由于本人早已不是键控技术党,** 13 | **而且没有精力和兴趣去学相关的键控技术知识。** 14 | 15 | **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 16 | 17 | **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 18 | 19 | **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 20 | 21 | 见 [天台十炮连接脚本](./scripts/tian_tai_10/tian_tai_10.cpp) 22 | 23 | [目录](./0catalogue.md) -------------------------------------------------------------------------------- /tutorial/15cob_manager_3.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # 炮管理类——铲种 9 | 10 | 本框架 中没有高级封装的 `FixCob` 函数,取而代之的是铲除函数 `Shovel` ,种炮函数 `Plant`,这两个函数的有机结合会比 `FixCob` 更加灵活,下面举例说明 11 | 12 | 首先介绍这两个函数的使用 13 | 14 | ```C++ 15 | // 原地铲种位于二行三列的炮 16 | Shovel(2, 3); 17 | aCobManager.Plant(2, 3); 18 | ``` 19 | 20 | 看起来是要比 `FixCob` 复杂了,因为 `FixCob` 只需要一条语句,而这需要两条语句,但是在 "炮管理类——初步" 中提到过,铲种炮不仅仅原地铲种一种形式,还有位移铲种等多种形式,如果使用 `FixCob` 来实现全部的铲种形式时,就会显得力不从心,但是这两个函数可以很容易的实现,例如: 21 | 22 | 23 | ```C++ 24 | // 位移铲种 25 | Shovel(2, 4); 26 | aCobManager.Plant(2, 3); 27 | 28 | // 超时空铲种(先种后铲) 29 | SetTime(-200, 2); 30 | aCobManager.Plant(2, 3); 31 | SetTime(-100, 2); 32 | Shovel(1, 4); 33 | ``` 34 | 35 | 这些都是 `FixCob` 很难以实现的,因此这种方式比 `FixCob` 灵活的多。 36 | 37 | 除 `Shovel` 和 `Plant` 之外,ACobManager 中还存在着一个铲种函数 `FixLatest`,即为修补上一枚发射的炮,使用示例如下: 38 | ```C++ 39 | aCobManager.Fire(2, 9); 40 | aCobManager.FixLatest(); 41 | ``` 42 | 43 | `FixLatest` 将会修补 `Fire` 刚刚发射出去的炮,此函数不用填写任何参数,直接调用即可,**但是其只支持原地铲种**,原因后续再进行介绍。 44 | 45 | 46 | [目录](./0catalogue.md) 47 | -------------------------------------------------------------------------------- /tutorial/16cob_manager_4.md: -------------------------------------------------------------------------------- 1 | 7 | # 炮管理类——炮序模式 8 | 9 | 在 ACobManager 类中,有一个十分重要的成员我们还未介绍,那就是 `SetSequentialMode`,在 本框架 中,有三种可用的炮序模式: 10 | 11 | - 时间模式(默认) 12 | - 空间模式 13 | - 优先级模式 14 | 15 | 如果想设定炮序使用模式,需要使用 `SetSequentialMode` 函数,示例如下: 16 | ```C++ 17 | // 将炮序模式设置为空间模式 18 | aCobManager.SetSequentialMode(ACobManager::SPACE); 19 | 20 | // 将炮序模式设置为优先级模式 21 | aCobManager.SetSequentialMode(ACobManager::PRIORITY); 22 | ``` 23 | ## 空间使用模式 24 | 在空间使用模式下,炮管理类对象将会完全按照炮列表的顺序(也就是 `SetList` 时指定的顺序)进行发射,第一次发射列表中的第一门炮。第二次发射第二门,以此类推。如果准备发射的这门炮尚未恢复则会报错。这种模式的好处在于我们每时每刻都知道将要发射的是哪门炮,但缺点是在涉及天台或铲种时,可能会需要比较烦人的炮序调整。 25 | 26 | ## 时间使用模式 27 | 时间使用模式是空间使用模式的升级版。在此模式下,如果某次发射时轮到的炮没有恢复,炮管理类对象会自动向后遍历列表,直到找到一门可用的炮为止。这种模式下原地铲种不需要调整炮序,但是同样会带来一个麻烦:我们可能无法提前知道某一时刻发射的是哪门炮,此时如果需要铲种就会比较麻烦。这种情况下,就需要调用前一个文章中的函数 `FixLatest` 了,炮管理类对象会自动铲种上一次发射的炮。因此如果解中只存在原地铲种且不关心铲种的位置时,推荐使用此模式与 `FixLatest` 的组合。 28 | 29 | ## 优先级使用模式 30 | 这种模式与时间使用模式都会在发射时遍历列表,但遍历逻辑不同。举个例子,如果每 40s 发射一门炮(这个间隔长于炮 CD),那么前两种模式会按照炮列表的顺序发射,而优先级模式会一直发射炮列表中的第一门炮。具体来说,在优先级模式下,炮管理类对象每次发炮都会**从头**开始扫描炮列表,找到第一门可以恢复的炮并发射。这种模式非常适合管理有位置要求的铲种。 31 | 32 | [目录](./0catalogue.md) 33 | -------------------------------------------------------------------------------- /tutorial/17cob_manager_5.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # 炮管理类——炮序排布 9 | 10 | 回到炮管理类设计之初要解决的问题——炮序排布。炮管理类内置了一些调整炮序的函数: 11 | 12 | ```C++ 13 | // 时间模式/空间模式: 14 | 15 | // 跳过炮列表中的两门炮 16 | aCobManager.Skip(2); 17 | // 将炮列表中位于二行三列中的炮设置为下一门即将发射的炮 18 | aCobManager.SetNext(2, 3); 19 | // 将炮列表中的第一门炮设置为下一门即将发射的炮 20 | aCobManager.SetNext(1); 21 | 22 | // 优先级模式: 23 | 24 | // 把位于 2-3 的炮的优先级设为最高 25 | aCobManager.MoveToListTop(2, 3); 26 | // 把位于 1-1、2-1 的炮的优先级设为最低 27 | aCobManager.MoveToListBottom({{1, 1}, {2, 1}}); 28 | ``` 29 | 30 | 由于不同模式的发炮逻辑不同,炮序调整函数**不互通**,使用与炮序模式不符的函数会报错。 31 | 32 | 接下来分几种情况讨论: 33 | 34 | ## 非天台场景 35 | 36 | 在非天台场景,不同位置的炮之间并没有本质差别。如果无需铲种,一个炮列表就可以应对所有情况。 37 | 38 | ### 原地铲种 + 不关心铲种位置 39 | 40 | 这种情况推荐大家使用 时间使用模式 + `FixLatest` 的组合,由于不关心铲种位置,这种组合可以说是最适合这种情况的了,可以省去几乎所有的麻烦。 41 | 42 | ### 关心铲种位置 43 | 44 | 这种情况下使用 `FixLatest` 就比较勉强甚至不可用了(比如需要位移铲种的情形),推荐使用优先级模式。既然我们要指定某炮在某时间发射,那么就在炮列表里把这门炮的优先级降到最低(这样炮管理类就会优先使用其他的炮,把这门炮空出来),在预定的时间点再把它的优先级调回最高即可。写成代码就是这样: 45 | 46 | ```C++ 47 | // 出怪、选卡等等 48 | 49 | aCobManager.SetSequentialMode(ACobManager::PRIORITY); 50 | aCobManager.MoveToListBottom(1, 1); // 假设要铲种 1-1 的炮 51 | 52 | // 其他操作 53 | 54 | // 假设要在这个时间铲种 55 | AConnect(ATime(3, 225 - 373), [] -> ACoroutine { 56 | aCobManager.MoveToListTop(1, 1); 57 | aCobManager.Fire(2, 9); 58 | co_await 205; // 炮发射 205cs 之后可以铲除 59 | AShovel(1, 1); 60 | aCobManager.Plant(1, 1); 61 | }) 62 | ``` 63 | 64 | ## 天台场景 65 | 66 | 当场景为天台场景时,多炮列表的存在就变的有意义了。在原版的天台场景中,玉米加农炮的飞行时间和落点会随着炮所在列数发生波动,因此如果只使用一个炮列表会使得炮序排布变得非常困难。 67 | 68 | ### 按列划分 69 | 70 | 这是最基本的一种划分炮列表的方式。以这个阵型为例: 71 | 72 | ![RE 18](./img/RE18.jpg) 73 | 74 | 我们可以这样做: 75 | 76 | ```C++ 77 | // 分配四个炮列表,分别是 78 | ACobManager col1, col3, col5, col7; 79 | 80 | void AScript(){ 81 | // 为其分配炮 82 | col1.SetList({{1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}}); 83 | col3.SetList({{1, 3}, {2, 3}, {3, 3}, {4, 3}, {5, 3}}); 84 | col5.SetList({{1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}}); 85 | col7.SetList({{1, 7}, {2, 7}, {3, 7}}); 86 | } 87 | ``` 88 | 89 | 这样我们使用相应的对象调用相应的炮函数就可以发射相应位置的炮了。这种方法永远不会出错,但代价是需要脚本编写者提前设计好完整的炮序,编写和修改都很麻烦。 90 | 91 | ### 按风炮、平炮划分 92 | 93 | 如果阵型对炮落点的要求不是很苛刻,也可以把炮列表简化为两个: 94 | 95 | ```C++ 96 | ACobManager slope, flat; 97 | 98 | void AScript(){ 99 | slope.SetList({{1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, 100 | {1, 3}, {2, 3}, {3, 3}, {5, 3}, {5, 3}}); 101 | flat.SetList({{1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}, 102 | {1, 7}, {2, 7}, {3, 7}}); 103 | } 104 | ``` 105 | 106 | ### 两用炮 107 | 108 | 3 列炮和 5 列炮往往既可以当作风炮用,也可以当作平炮用。但是,如果简单粗暴地让 `slope` 包含 1、3、5 列炮,`flat` 包含 3、5、7 列炮,会带来一个问题:这两个炮列表可能会放着“专属”的 1 列和 7 列炮不用而去抢两用炮,这显然是我们不希望看到的。可以使用优先级模式解决这个问题: 109 | 110 | ```C++ 111 | ACobManager slope(ACobManager::PRIORITY), flat(ACobManager::PRIORITY); 112 | 113 | void AScript(){ 114 | // slope 优先用 1 列,其次用 3 列,最后用 5 列 115 | slope.SetList({{1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, 116 | {1, 3}, {2, 3}, {3, 3}, {5, 3}, {5, 3}, 117 | {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}}); 118 | // flat 反过来,优先用 7 列,其次用 5 列,最后用 3 列 119 | flat.SetList({{1, 7}, {2, 7}, {3, 7}, 120 | {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}, 121 | {1, 3}, {2, 3}, {3, 3}, {5, 3}, {5, 3}}); 122 | } 123 | ``` 124 | 125 | 当然,这种方法也不是万能的,有时仍然需要人工干预炮序。 126 | 127 | ## 智能炮序排布 128 | 129 | 在一些更为复杂的情境下,我们可能希望脚本在运行时动态决定发炮逻辑。炮管理类提供了几个获取炮冷却信息的函数: 130 | 131 | ```C++ 132 | // 得到可用的炮的内存指针 133 | auto cobPtr = aCobManager.GetUsablePtr(); 134 | 135 | // 得到发往九列可用的屋顶炮的内存指针 136 | auto cobPtr = aCobManager.GetRoofUsablePtr(9); 137 | 138 | // 得到恢复时间最短的炮的内存指针 139 | auto cobPtr = aCobManager.GetRecoverPtr(); 140 | 141 | // 得到发往九列恢复时间最短的屋顶炮的内存指针 142 | auto cobPtr = aCobManager.GetRoofRecoverPtr(9); 143 | 144 | // 得到 1-2 列的屋顶炮发往 7 列飞行的时间 145 | auto flyTime = ACobManager::GetRoofFlyTime(1, 7); 146 | 147 | // 得到恢复时间列表 148 | // 如果恢复时间为 -1,代表炮被锁了不可用 149 | // 如果恢复时间为 INT_MIN,代表炮消失了 150 | // 恢复时间 >= 0 为正常值 151 | auto lst = aCobManager.GetRecoverList(); 152 | for (auto&& [ptr, recoverTime] : lst) { 153 | // 这里应该写相应的处理代码 154 | // 示例就打印一下算了 155 | logger.Info("指针: #, 恢复时间: #", ptr, recoverTime); 156 | } 157 | ``` 158 | 159 | 炮序排布的方法有很多,而且并没有绝对的好坏。上面的内容只是基于作者的个人观点,有不同意见欢迎讨论。 160 | 161 | [目录](./0catalogue.md) 162 | -------------------------------------------------------------------------------- /tutorial/18connector.md: -------------------------------------------------------------------------------- 1 | 7 | # 连接再探 8 | 9 | 本页教程将说明 AConnect 函数的所有用法,希望大家能够熟练的运用。 10 | 11 | 12 | ## 连接按键和 bool Functor() 13 | ```C++ 14 | 15 | // *** 使用示例: 16 | ALogger logger; 17 | AConnectHandle keyHandle; 18 | 19 | void AScript() 20 | { 21 | logger.SetLevel({ALogLevel::INFO}); 22 | 23 | // 按下 0 键弹出一个窗口, 显示 hello, 注意 0 是单引号 24 | // 运行方式为 true 时, AConnect 创建的连接选卡界面和高级暂停时都生效, 反之不生效 25 | // *** 特别注意:如果连接创建失败, 连接控制器将被赋值为 nullptr. 26 | keyHandle = AConnect('0', [] { logger.Info("hello"); }); 27 | 28 | // 按下 E 键控制 0 键的是否暂停 29 | // 如果 0 键此时暂停生效, 按下 E 键便会继续生效, 反之相反 30 | // 注意字母必须是大写 31 | AConnect('E', [] { 32 | if (keyHandle.isPaused()) { 33 | keyHandle.GoOn(); 34 | } else { 35 | keyHandle.Pause(); 36 | } 37 | }); 38 | 39 | // 按下 Q 键控制 0 键行为, 即将 0 键的显示变为 world 40 | AConnect('Q', [] { 41 | keyHandle.Stop(); // 注意此时 keyHandle 已失效 42 | keyHandle = AConnect('0', [] { logger.Info("world"); }); // 此时 keyHandle 重新有效 43 | }); 44 | 45 | // AConnect 第一个参数还可传入一个 bool Functor(), 如果此函数返回 true, 则会执行后面的操作 46 | // 这个示例就是游戏每 10 秒钟显示一个 world 的窗口 47 | AConnect([] { return AGetMainObject()->GameClock() % 1000 == 0; }, [] { logger.Info("world"); }); 48 | } 49 | ``` 50 | 51 | 看到这里,你可能疑惑是否能让 AConnect 绑定鼠标左键,答案是可以。 52 | 53 | 但是你需要查看 : 虚拟键盘表 https://learn.microsoft.com/zh-cn/windows/win32/inputdev/virtual-key-codes 54 | 55 | 比如虚拟键盘表中的鼠标左键为 `VK_LBUTTON` 56 | 57 | 那么如果你想绑定左键就可以这么写 58 | 59 | ```C++ 60 | AConnect(VK_LBUTTON, [] { /* some code */}); 61 | ``` 62 | 63 | 64 | ## 连接时间 65 | 66 | **警告,如果你是本框架的初学者,并且暂时不想了解比较复杂的时间管理方式,建议先不要看下面的内容,因为下面这些语句的语法看起来可读性比较差** 67 | 68 | AConnect 的 ATime 调用形式是有返回值的, 69 | ```C++ 70 | auto timeHandle = AConnect(ATime(1, 1), []{}); 71 | timeHandle.Stop(); // 使建立的连接的失效,但是没有 Pause 这些成员 72 | ``` 73 | 74 | 关于时间连接还有两个不得不介绍的函数,那就是 `ANowTime` 和 `ANowDelayTime` , 75 | 76 | ```C++ 77 | // ANowTime 会返回当前的游戏时间点 78 | 79 | // 无参调用形式返回值类型为 ATime 80 | // 如果当前游戏不在战斗界面,他将会返回 (1, INT_MIN) 这么一个特殊的时间点, 81 | // 如果游戏在战斗界面,他将会返回 (游戏目前波数,波数对应的时间点) 82 | ANowTime(); 83 | 84 | // 返回第五波对应的时间点 85 | // 有参调用形式返回值类型为 int 86 | ANowTime(5); 87 | 88 | // ANowDelayTime 会返回以当前时间点为基准,延迟 参数cs 之后的时间点 89 | 90 | // 仅指定 delayTime 的返回值类型为 ATime 91 | // 假如现在的时间点是 (1, 0),那么此函数返回值为 ATime(1, 50) 92 | ANowDelayTime(50); 93 | 94 | // 指定波数和 delayTime 的返回值类型为 int 95 | // 假如现在的时间点是 (5, -150),那么此函数返回值为 -100 96 | ANowDelayTime(5, 50); 97 | 98 | ``` 99 | 100 | ```C++ 101 | // 按下 Q 后过 100cs 种下一张卡 102 | AConnect('Q', [] { 103 | AConnect(ANowDelayTime(100), [] { ACard(1, 1, 1); }); 104 | }); 105 | ``` 106 | 107 | [目录](./0catalogue.md) 108 | -------------------------------------------------------------------------------- /tutorial/19coroutine.md: -------------------------------------------------------------------------------- 1 | 7 | # 阻塞再探 8 | 9 | 本页教程将说明 co_await 的所有用法,希望大家能够熟练的运用。 10 | 11 | 在正式介绍阻塞时,咱们首先得说明一下协程是什么,大家可以把协程理解成一个线程, 12 | 线程咱们知道能够并行运行,相应的,协程能够并发运行,注意并发和并行并不是一个东西, 13 | 至于有什么区别,这个和本框架使用协程没有任何关系,所以咱们就不详细的解释了, 14 | 咱们接下来写一个小示例。 15 | 16 | ```C++ 17 | #include 18 | 19 | // 需要看教程 [日志功能] 20 | ALogger console; 21 | 22 | ACoroutine Func1() 23 | { 24 | co_await ANowDelayTime(100); 25 | console.Info("Func1() 100"); 26 | co_await ANowDelayTime(300); 27 | console.Info("Func1() 300"); 28 | } 29 | 30 | ACoroutine Func2() 31 | { 32 | co_await ANowDelayTime(200); 33 | console.Info("Func2() 200"); 34 | co_await ANowDelayTime(400); 35 | console.Info("Func2() 400"); 36 | } 37 | 38 | void AScript() 39 | { 40 | AWaitForFight(); 41 | // 协程需要使用 ACoLaunch 或者 AConnect 启动 42 | // ACoLaunch 是立即启动协程 43 | // AConnect 是按照设定的时间或者条件启动协程 44 | ACoLaunch(Func1); 45 | ACoLaunch(Func2); 46 | } 47 | ``` 48 | 49 | ```plain 50 | [1, -2147483648][INFO] 51 | ================================= 52 | 脚本开始运行 53 | ================================= 54 | [1, -499][INFO] Func1() 100 55 | [1, -399][INFO] Func2() 200 56 | [1, -199][INFO] Func1() 300 57 | [1, 1][INFO] Func2() 400 58 | ``` 59 | 60 | 我相信大家应该能看懂脚本都写的是啥意思,咱们主要看运行结果, 61 | 可以发现所有语句都在正确的时间点执行了,Func1() 和 Func2() 62 | 是互不干扰的,这一点可以从日志的打印时间点看出来,因此, 63 | **Func1() 和 Func2() 是并行执行的,他们的表现就像线程一样**, 64 | 理解这点十分重要,那么下面的代码,可以想一下运行结果是啥 65 | 66 | ```C++ 67 | #include 68 | 69 | ALogger console; 70 | 71 | ACoroutine Func1() 72 | { 73 | co_await ATime(1, -399); 74 | console.Info("Func1() 1, -399"); 75 | co_await ATime(1, -599); 76 | console.Info("Func1() 1, -599"); 77 | } 78 | 79 | void AScript() 80 | { 81 | AWaitForFight(); 82 | ASetInternalLogger(console); 83 | ACoLaunch(Func1); 84 | } 85 | ``` 86 | 87 | 运行结果是: 88 | 89 | ```plain 90 | [1, -2147483648][INFO] 91 | ================================= 92 | 脚本开始运行 93 | ================================= 94 | [1, -599][INFO] 建立 时间-操作 [1, -399] 连接成功 95 | [1, -399][INFO] 运行 时间-操作 [1, -399] 96 | [1, -399][INFO] Func1() 1, -399 97 | [1, -399][WARNING] 时间-操作 [1, -599] 但是现在时间已到 [1, -399] 98 | [1, -399][INFO] 运行 时间-操作 [1, -599] 99 | [1, -399][INFO] Func1() 1, -599 100 | [1, -399][INFO] 协程退出 101 | 102 | ``` 103 | 104 | 可以看到,协程函数内部的阻塞是要按照时间顺序书写的,时间顺序反了就没有办法运行, 105 | 这一点就是阻塞的缺点,之前其实早就提到了,好了,关于协程是怎么运行的咱们就介绍完了, 106 | 现在的关键问题是:既然他有这样的缺点,所以这玩意到底有什么用?咱们看下面的代码: 107 | 108 | 常年写自动挂机脚本的同志可能写出下面的代码 109 | 110 | ```C++ 111 | bool IsHasGiga() 112 | { 113 | // aAliveZombieFilter 用法具体查看教程中的 [对象过滤迭代器] 114 | for (auto&& zombie : aAliveZombieFilter) { 115 | int zombieRow = zombie.Row(); 116 | // 如果僵尸类型为红眼并且在上半场 117 | if (zombie.Type() == AGIGA_GARGANTUAR 118 | && (zombieRow == 0 || zombieRow == 1)) { 119 | return true; 120 | } 121 | } 122 | return false; 123 | } 124 | 125 | void AScript() 126 | { 127 | // 按下 Q 进行收尾操作 128 | AConnect('Q', [] { 129 | // 如果没有红眼直接返回 130 | if (!IsHasGiga()) { 131 | return; 132 | } 133 | aCobManager.Fire(2, 9); 134 | // 延迟 373 看看一炮下去还有没有红眼僵尸存活 135 | AConnect(ANowDelayTime(373), [] { 136 | if (!IsHasGiga()) { 137 | return; 138 | } 139 | // 如果还有红眼,再来一炮 140 | aCobManager.Fire(2, 9); 141 | 142 | // 延迟 373 看看一炮下去还有没有红眼僵尸存活 143 | AConnect(ANowDelayTime(373), [] { 144 | if (!IsHasGiga()) { 145 | return; 146 | } 147 | // 如果还有红眼,再来一炮 148 | aCobManager.Fire(2, 9); 149 | }); 150 | }); 151 | }); 152 | } 153 | ``` 154 | 155 | 上述代码你会发现非常多的递归嵌套,如果再多个几层可能会直接要了脚本可读性的命, 156 | 那么协程阻塞就是为了解决这个事情的,看下面的代码 157 | 158 | ```C++ 159 | #include 160 | 161 | bool IsHasGiga() 162 | { 163 | // aAliveZombieFilter 用法具体查看教程中的 [对象过滤迭代器] 164 | for (auto&& zombie : aAliveZombieFilter) { 165 | int zombieRow = zombie.Row(); 166 | // 如果僵尸类型为红眼并且在上半场 167 | if (zombie.Type() == AGIGA_GARGANTUAR 168 | && (zombieRow == 0 || zombieRow == 1)) { 169 | return true; 170 | } 171 | } 172 | return false; 173 | } 174 | 175 | void AScript() 176 | { 177 | 178 | // 按下 Q 进行收尾操作 179 | AConnect('Q', []() -> ACoroutine { 180 | // 一共尝试三次 181 | for (int i = 0; i < 3; ++i) { 182 | if (!IsHasGiga()) { 183 | co_return; 184 | } 185 | aCobManager.Fire(2, 9); 186 | if (i != 2) { // 最后一次不需要再进行阻塞延迟了 187 | co_await ANowDelayTime(373); 188 | } 189 | } 190 | }); 191 | } 192 | ``` 193 | 194 | 请注意这个 AConnect 函数右边可是 `[]() -> ACoroutine` 一定要写成这样,不然没法 195 | 使用 co_await,可以看到使用连接和阻塞的结合,再也没有了嵌套调用, 196 | 代码的可读性一下就变高了,上述 for 循环尝试了三次,如果这三次尝试不用阻塞, 197 | 纯用连接,那么就会有三次 AConnect 的嵌套调用,想一想就令人头皮发麻。 198 | 199 | 咱们再举一个例子,就是实现卡片好了立即使用卡片这个功能,如果不使用阻塞,就是接下来这种写法, 200 | 你需要注意很多的细节,比如为啥要用动态内存申请? (`std::make_shared()`), 201 | 为啥这里不能用全局的 ATickRunner 对象? 为啥不能用局部的 ATickRunner 对象? 202 | 这里就不解释了,因为涉及到的知识点比较多,而且解释这个东西不是本节的重点。 203 | 204 | ```C++ 205 | void RecoverCard(APlantType plant, int row, float col) 206 | { 207 | // 卡片好了直接种 208 | if (AIsSeedUsable(plant)) { 209 | ACard(plant, row, col); 210 | return; 211 | } 212 | // 卡片没好,咱们得进行检测 213 | // 注意这里直接用 cd 的方式可能是不对的,因为还有阳光的限制,因此咱们得每帧检测 214 | // AIsSeedUsable 会检测卡片是否能种 215 | // 至于这个 std::make_shared 是啥,懂得自然懂,不懂的也不需要懂 216 | // 因为咱们不需要这种写法了 217 | auto tickRunner = std::make_shared(); 218 | tickRunner->Start([=] { 219 | if (AIsSeedUsable(plant)) { 220 | ACard(plant, row, col); 221 | tickRunner->Stop(); 222 | } 223 | }); 224 | } 225 | ``` 226 | 227 | 咱们再看协程阻塞版本 228 | 229 | ```C++ 230 | void RecoverCard(APlantType plant, int row, float col) 231 | { 232 | // 由于本框架实现的协程只能是无参形式,所以只能通过 Lambda 捕获的方式模拟有参协程 233 | ACoLaunch([=]() -> ACoroutine { 234 | // 卡片好了直接种 235 | if (AIsSeedUsable(plant)) { 236 | ACard(plant, row, col); 237 | co_return; // 注意这里是 co_return,协程里面不能用 return 238 | } 239 | // 等待卡片能用 240 | co_await [=] { return AIsSeedUsable(plant); }; 241 | ACard(plant, row, col); 242 | }); 243 | } 244 | ``` 245 | 246 | 脚本中的注意点我都写在注释里面了,大家可以看到使用了协程阻塞代码瞬间就变得清爽了起来, 247 | 直接就是一个符合人类阅读的等待卡片能用,然后直接种卡片就行了,非常舒服, 248 | 这里我需要解释一下 co_await 249 | 后面跟的是啥玩意, 250 | 251 | co_await 是可以跟两个东西的 252 | 253 | * ATime(x, y) : 意思为等到时间点到达 (x, y) 时释放阻塞 254 | * bool Functor() : 意思是当这个 Functor 返回 true 的时候释放阻塞 255 | 256 | 很明显上面这个 `co_await [=] { return AIsSeedUsable(plant); };` 就是用了第二种形式 257 | ,那么说到这里,我相信你已经完全明白了 co_await 巨大优势, 258 | 它本质上可以理解为创建了一个小线程,这个小线程阻不阻塞和外面的世界没有任何关系。 259 | 260 | 最后再来一个例子吧 261 | 262 | ```C++ 263 | // 纯连接版本 264 | // 按下 Q 后过 100cs 种下一张卡 265 | AConnect('Q', [] { 266 | AConnect(ANowDelayTime(100), [] { ACard(1, 1, 1); }); 267 | }); 268 | 269 | // 协程阻塞版本 270 | // 按下 Q 后过 100cs 种下一张卡 271 | AConnect('Q', []()-> ACoroutine { 272 | co_await ANowDelayTime(100); 273 | ACard(1, 1, 1); 274 | }); 275 | ``` 276 | 277 | 还是那个说法,协程阻塞拥有比纯连接更高的可读性。 278 | **因此咱们外层使用 AConnect, AConnect 内部使用协程,也就是整体连接,局部阻塞**, 279 | 这一下就把两者的优缺点进行了强势互补,为什么咱们不喜欢用阻塞, 280 | 就是因为咱们得根据时间点调整咱们的代码书写顺序,但是现在咱们配合连接可以做到 281 | **只是在一个小局部使用阻塞,这样阻塞的劣势将变得几乎没有,因为就一个小局部,时间点太好调了**。 282 | 而且也保证了脚本代码的可读性,这就是协程阻塞的真正威力。 283 | 284 | [目录](./0catalogue.md) 285 | -------------------------------------------------------------------------------- /tutorial/20game_controllor.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # 游戏控制函数 9 | 10 | ## 高级暂停 11 | 12 | 高级暂停功能由 `ASetAdvancedPause` 提供。 13 | 14 | ```C++ 15 | // 设定高级暂停 16 | // *** 注意开启高级暂停时不能使用跳帧 17 | // *** 特别注意的是 `ASetAdvancedPause` 一旦使得程序进入高级暂停状态后, 18 | // 除了模式为 GLOBAL 和 AFTER_INJECT 的帧运行以及 AfterTick BeforeTick 的状态钩,框架将不再执行其他代码, 19 | // *** 使用示例 20 | // ASetAdvancedPause(true) ------ 开启高级暂停 21 | // ASetAdvancedPause(false) ------ 关闭高级暂停 22 | void ASetAdvancedPause(bool isAdvancedPause); 23 | 24 | // 一般来说,这个函数需要配合 AConnect 使用 25 | // 按下 Q 进行高级暂停 26 | AConnect('Q', []{ 27 | ASetAdvancedPause(true); 28 | }); 29 | 30 | // 按下 W 停止高级暂停 31 | AConnect('W', []{ 32 | ASetAdvancedPause(false); 33 | }); 34 | 35 | 36 | // 你仍然可以这样做 ,自己整一个 static 变量 37 | // 至于 static 变量是啥作用,你需要自行百度 38 | // 简单理解就是这个变量离开作用域也不会消失,且只会初始化一次 39 | // 那么下面的代码就是实现了这样的功能 40 | // 如果游戏不是高级暂停,按下 Q 就高级暂停,如果是高级暂停,按下 Q 就不会高级暂停 41 | AConnect('Q', []{ 42 | static bool isPaused = false; 43 | isPaused = !isPaused; 44 | ASetAdvancedPause(isPaused); 45 | }); 46 | 47 | ``` 48 | 49 | ## 跳帧 50 | 51 | 跳帧是节省脚本调试和游戏冲关时间的重要功能,需要使用 `ASkipTick` 来使用此功能。 52 | 53 | ### 跳到指定时间 54 | 55 | ```C++ 56 | // 跳到游戏时刻点 (2, 300) 57 | ASkipTick(2, 300); 58 | ``` 59 | 使用这条语句游戏将会跳到 (2, 300),注意在此过程中,游戏画面是不会刷新的(游戏画面直接卡住), 60 | 游戏内部会以上几十甚至几百倍运行(由 CPU 性能决定),以 i5-7300HQ 为例,运行速度大概为 40 倍, 61 | 当游戏时间进行到 (2, 300) 时,游戏会回复正常运行状态。 62 | 63 | ### 跳到指定条件 64 | 65 | ```C++ 66 | // 直接跳过整个游戏 67 | ASkipTick([=](){ 68 | return true; 69 | }); 70 | ``` 71 | 除了可以指定时间外,还可以指定条件,当返回 `true` 时,游戏帧将会被跳过去,当返回 `false` 时,跳帧将会停止, 72 | 由于上述代码一直返回 `true`,因此所有游戏帧都将会被跳过去。 73 | 74 | ```C++ 75 | // 使得游戏接近 50 倍速运行 76 | // 原理解释:当游戏时钟对 50 取余值为 0 时,触发此连接的操作函数, 77 | // 操作函数运行即进行跳帧,跳帧依然对 50 取余,当此值为 0 时跳帧结束 78 | // 然后取余 0 的下一帧值又为 1,又触发了连接,便又开始跳帧, 79 | // 也就是游戏每隔 50cs 启用一次跳帧,跳帧会跳过 49 帧,这便突破了 ASetGameSpeed 10 倍以上的游戏运行速度 80 | AConnect([] { return AGetMainObject()->GameClock() % 50 == 0; }, [] { 81 | ASkipTick([] { return AGetMainObject()->GameClock() % 50 != 0; }); 82 | }); 83 | ``` 84 | 85 | ```C++ 86 | // 检测位于 {1, 3}, {1, 5} 的玉米炮是否被破坏,如果被破坏,停止跳帧 87 | auto condition = [=]() { 88 | std::vector results; 89 | AGetPlantIndices({{1, 3}, {1, 5}}, AYMJNP_47, results); 90 | 91 | for (auto result : results) { 92 | if (result < 0) { 93 | return false; 94 | } 95 | } 96 | return true; 97 | }; 98 | 99 | ASkipTick(condition); 100 | ``` 101 | 上述代码是检测破阵的跳帧使用示例,假如位于 {1, 3}, {1, 5} 的玉米炮被破坏就意味着此解失败, 102 | 那么上述代码会使得游戏直接跳到玉米炮被损坏的时候。 103 | 104 | ```C++ 105 | auto condition = [=]() { 106 | std::vector results; 107 | AGetPlantIndices({{1, 3}, {1, 5}}, AYMJNP_47, results); 108 | 109 | for (auto result : results) { 110 | if (result < 0) { 111 | return false; 112 | } 113 | } 114 | return true; 115 | }; 116 | 117 | auto callback = [=]() { 118 | // 写春哥没了的提示代码,比如用一个 ALogger 显示信息 119 | }; 120 | 121 | ASkipTick(condition, callback); 122 | ``` 123 | 124 | 当然 `ASkipTick` 还可以使用回调函数,就是当停止跳帧时立马执行的函数,上述代码执行结果: 125 | 当位于 {1, 3}, {1, 5} 的玉米炮被破坏时报出错误:"春哥无了,嘤嘤嘤"。 126 | 127 | 128 | 129 | ## 快速进入游戏 130 | 快速进入游戏由函数 `AEnterGame` 实现,此函数可以节省进入游戏的手动操作时间 131 | 132 | ```C++ 133 | // 快速进入游戏函数 134 | // *** 使用示例 135 | // AEnterGame() ------ 默认进入泳池无尽生存模式,默认会自动点掉继续对话框 136 | // AEnterGame(AAsm::SURVIVAL_ENDLESS_STAGE_1) -------- 进入白天无尽生存模式,默认会自动点掉继续对话框 137 | // AEnterGame(AAsm::SURVIVAL_ENDLESS_STAGE_1, false) -------- 进入白天无尽生存模式,不会自动点掉继续对话框 138 | inline void AEnterGame(int gameMode = AAsm::SURVIVAL_ENDLESS_STAGE_3, bool hasContinueDialog = false); 139 | ``` 140 | 141 | ## 快速回到游戏主界面 142 | ```C++ 143 | // 快速回到游戏主界面 144 | // *** 注意:此函数仅在战斗界面生效 145 | // *** 使用示例 146 | // ABackToMain() ----- 直接回到主界面,默认会自动存档 147 | // ABackToMain(false) ----- 直接回到主界面,不会自动存档 148 | inline void ABackToMain(bool isSaveData = true); 149 | ``` 150 | 151 | 以上两个功能可以合并一下实现一个非常懒狗的功能 152 | ```C++ 153 | // 按下 Q 键直接返回主界面,然后又从主界面立即进入游戏选卡或者战斗界面 154 | // 所以当存档设置为只读的时候,此功能就非常懒狗 155 | AConnect('Q', [] { 156 | ABackToMain(); 157 | AEnterGame(); 158 | }); 159 | ``` 160 | 161 | [目录](./0catalogue.md) 162 | -------------------------------------------------------------------------------- /tutorial/21read_memory_basic.md: -------------------------------------------------------------------------------- 1 | 7 | # 内存读取初步 8 | 9 | 有时候,我们希望脚本根据场上的情况“智能”地作出行动。为达到这个目的,需要读取游戏内存中的各种数据。本节会介绍如何读取 AvZ 中已经封装好的游戏数据。 10 | 11 | ## `AMainObject` 12 | 13 | `AMainObject` 对应的是游戏战斗界面的内存,里面储存着几乎所有和战斗有关的信息。读取数据的代码长这样: 14 | 15 | ```C++ 16 | // 启用一个控制台日志对象 17 | // 关于如何使用日志,请查看教程目录,日志功能 18 | ALogger logger; 19 | // 读取当前阳光数 20 | int sun = AGetMainObject()->Sun(); 21 | // 设置阳光为 8000 22 | AGetMainObject()->Sun() = 8000; 23 | 24 | AConnect(ATime(20, 0), []{ 25 | // 对于非阻塞式脚本,内存读取一般放在 AConnect 里 26 | // 变量 sun 的值不会随着游戏进行而更新,而是定格为战斗开始时的阳光值 27 | // 为了正确显示出 w20 时的阳光,需要在 AConnect 里调用读内存函数 28 | logger.Info("当前阳光: #", AGetMainObject()->Sun()); 29 | }); 30 | 31 | // 这样写是不行的!AMainObject(以及后面介绍的各种对象)都直接对应 PvZ 的内存,这也就意味着不能复制这些对象 32 | // AMainObject mo = *AGetMainObject(); 33 | // 如果不理解 C++ 里什么时候会复制对象的话,只需要记住一个简单的原则:用指针变量存储这一类对象 34 | AMainObject* mo = AGetMainObject(); 35 | std::vector zombies; 36 | ``` 37 | 38 | ## 对象过滤器 39 | 40 | 对象过滤器是对 PvZ 中的各种对象数组的封装,可以遍历场上的所有对象。对象过滤器支持的对象类型有: 41 | 42 | - 植物(`APlant`) 43 | - 僵尸(`AZombie`) 44 | - 卡片(`ASeed`) 45 | - 场地物品(`APlaceItem`) 46 | 47 | ```C++ 48 | int cobCnt = 0; 49 | // 这个 for 循环遍历了场上所有存活的植物 50 | for (auto& plant : aAlivePlantFilter) { 51 | // 注意:这里访问属性用的是 . 而不是 -> 52 | if (plant.Type() == ACOB_CANNON) { 53 | // plant 的类型是 APlant&,需要写 &plant 把引用转化成指针 54 | bool isAvailable = (AGetCobRecoverTime(&plant) == 0); 55 | cobcnt += isAvailable; 56 | } 57 | } 58 | aLogger->Info("当前有 {} 门炮可用", cobCnt); 59 | 60 | // 如果一个比较复杂的条件多次被使用,可以预先定义一个附带条件的对象过滤器 61 | // 条件过滤器定义后可以随时使用,什么时候使用就基于什么时候的数据遍历,不像变量一样定义了之后就不会改了 62 | AAliveFilter gigaWithImp([](AZombie* zombie) { 63 | return zombie->Type() == AGIGA_GARGANTUAR && zombie->Hp() > 3000; 64 | }); 65 | 66 | for (auto& zombie : gigaWithImp) { 67 | // ... 68 | } 69 | 70 | // aAliveSeedFilter 只遍历当前可用的卡片。想遍历所有的卡片,需要使用 ABasicFilter 71 | // ABasicFilter 会无条件遍历所有对象(其中可能有无效对象,虽然遍历卡片时是不会有的),需要自行处理 72 | for (auto& seed : ABasicFilter()) { 73 | // ... 74 | } 75 | 76 | /* 77 | 场地物品大家可能不太熟悉,它包含以下几类对象: 78 | 1 - 墓碑 79 | 2 - 弹坑 80 | 3 - 梯子 81 | 4 - 传送门(圆) 82 | 5 - 传送门(方) 83 | 6 - 脑子(水族馆) 84 | 7 - 罐子 85 | 11 - 钉耙 86 | 12 - 脑子(我是僵尸) 87 | 这个列表也出现在了 APlaceItem::Type() 的文档中。左侧的数字代表对象 Type() 的值。 88 | */ 89 | // 吞掉场上的墓碑 90 | for (auto& item : aAlivePlaceItemFilter) { 91 | if (item.Type() == 1) { 92 | ACard(AGRAVE_BUSTER, item.Row() + 1, item.Col() + 1); 93 | break; 94 | } 95 | } 96 | ``` 97 | 98 | ## 场地信息 99 | 100 | AvZ 提供了 `aFieldInfo` 对象,可以方便地读取当前场地的信息。这种方式比使用 `AGetMainObject()->Scene()` 兼容性更好,也更方便。 101 | 102 | ```C++ 103 | enum class ARowType { 104 | NONE = 0, // 不能种植,不出僵尸 105 | LAND = 1, // 陆地 106 | POOL = 2, // 水池 107 | UNSODDED, // 不能种植,出僵尸 108 | }; 109 | 110 | struct AFieldInfo { 111 | int nRows; // 目前游戏中使用的行数 112 | int rowHeight; // 一行有多高 113 | ARowType rowType[7]; // 每行的类型 114 | bool isNight; // 是否是夜晚 115 | bool isRoof; // 是否是屋顶 116 | bool hasGrave; // 是否出墓碑 117 | bool hasPool; // 是否有泳池 118 | } aFieldInfo; 119 | 120 | // 注意 rowType 的索引是 1~6,不是 0~5 121 | ``` 122 | 123 | [目录](./0catalogue.md) 124 | -------------------------------------------------------------------------------- /tutorial/22tick_runner.md: -------------------------------------------------------------------------------- 1 | 7 | # 帧运行 8 | 9 | 在键控中,尤其是无炮键控中,我们常常需要某些操作时刻运行,那么这个时候我们就需要使用帧运行 `ATickRunner` 了 10 | 11 | 我们以自动三叶草程序为例,来介绍 ATickRunner 是如何使用的 12 | 13 | ```C++ 14 | #include 15 | 16 | void UseBlover() 17 | { 18 | for (auto&& zombie : aAliveZombieFilter) { 19 | if (zombie.Type() == AQQ_16 && zombie.Abscissa() <= 50) { // 如果有气球僵尸的横坐标小于50 (快飞到家了) 20 | // 这句话是获得三叶草卡片的内存指针 21 | // AGetCardIndex 函数就是从卡片名称得到卡片在卡槽位置的函数 22 | auto blover = AGetMainObject()->SeedArray() + AGetCardIndex(ASYC_27); 23 | if (blover->IsUsable()) { // 三叶草可用时才能吹气球 24 | ACard(ASYC_27, 1, 1); 25 | return; // 吹一次就结束了,因为再循环遍历也没用了 26 | } 27 | } 28 | } 29 | } 30 | 31 | ATickRunner tickRunner; 32 | 33 | void AScript() 34 | { 35 | tickRunner.Start(UseBlover); 36 | } 37 | ``` 38 | 39 | 相信这个程序很简单吧,大家应该都能看懂,但是实际上帧运行的 Start 函数还有一个参数,就是以何种方式运行,这是什么意思呢,看代码 40 | 41 | ```C++ 42 | tickRunner.Start(UseBlover); // 默认值只在战斗界面运行 43 | tickRunner.Start(UseBlover, ATickRunner::GLOBAL); // 在选卡和战斗界面运行 44 | tickRunner.Start(UseBlover, ATickRunner::AFTER_INJECT); // 在注入之后的每帧都运行 45 | ``` 46 | 47 | 实际上大多数情况下, 默认在战斗界面运行是足够用的,但是极少数情况下是需要再全局方式下运行,你可能会问,是什么情况下呢, 48 | 例如你觉得本框架自带的选卡函数不好的时候,就可以选择在全局方式下运行,对没错,只有在全局方式下运行,你才能选卡,是不是? 49 | 当然,还可能有其他作用,但是目前还不知道,反正他有一个这样的功能,你知道就行。 50 | 51 | [目录](./0catalogue.md) -------------------------------------------------------------------------------- /tutorial/23painter.md: -------------------------------------------------------------------------------- 1 | 7 | # 绘制类 8 | 9 | 本框架中支持对 PvZ 的画面进行绘制,使用此功能需要使用绘制类 `APainter` 10 | 11 | 咱们先看看他的接口 12 | 13 | ```C++ 14 | // 设置字体 15 | // 使用示例 16 | // SetFont("黑体") ------ 将字体设置为黑体 17 | void SetFont(const std::string& name); 18 | 19 | // 设置字体大小 20 | // 使用示例 21 | // SetFontSize(15) ------ 将字体大小设置为 15 22 | // 注意此处字体大小不一定与 MS Word 中的相同 23 | void SetFontSize(int size); 24 | 25 | // 设置文本颜色 26 | // 使用示例 27 | // 注意每个参数范围为 [0, 255] 28 | // SetTextColor(AArgb(0xff, 0, 0, 0)) ----- 将文本的不透明度设置为 0xff, 也就是不透明, 色彩设置为 RGB(0, 0, 0), 也就是黑色 29 | void SetTextColor(DWORD color); 30 | 31 | __ANodiscard DWORD GetTextColor(); 32 | 33 | // 设置矩形框颜色 34 | // 使用示例 35 | // 注意每个参数范围为 [0, 255] 36 | // SetRectColor(AArgb(0xff, 0, 0, 0)) ----- 将矩形框的不透明度设置为 0xff, 也就是不透明, 色彩设置为 RGB(0, 0, 0), 也就是黑色 37 | void SetRectColor(DWORD color); 38 | 39 | __ANodiscard DWORD GetRectColor(); 40 | 41 | // 绘制函数 42 | // 第一个参数指的是绘制什么: 文本还是矩形 43 | // 第二个参数指的是绘制的持续时间 44 | // 绘制文本 45 | // ***使用示例 46 | // Draw(AText("hello", 100, 100)) ------ 在游戏画面(100, 100) 处绘制 hello 47 | // Draw(AText("hello", 100, 100, RIGHT_TOP)) ------ 在游戏画面(100, 100) 处绘制 hello, 并且文本绘制是在 (100, 100) 的右上方 48 | // 绘制矩形 49 | // ***使用示例 50 | // Draw(ARect(100, 100, 200, 200)) ------ 在游戏画面(100, 100) 处绘制宽高为 (200, 200) 的矩形, 默认显示 1cs 51 | // Draw(ARect(100, 100, 200, 200), 100) ------ 在游戏画面(100, 100) 处绘制宽高为 (200, 200) 的矩形, 显示 100cs 52 | void Draw(const ARect& rect, const ARunOrder& runOrder = ARunOrder(0)); 53 | void Draw(const AText& posText, const ARunOrder& runOrder = ARunOrder(0)); 54 | void Draw(const ARect& rect, int duration, const ARunOrder& runOrder = ARunOrder(0)); 55 | void Draw(const AText& posText, int duration, const ARunOrder& runOrder = ARunOrder(0)); 56 | ``` 57 | 58 | 其实这里面用的最多的是最后的 Draw 函数,Draw 函数我们可以到看到有四个重载方式,在介绍 Draw 函数之前,我们首先需要知道 ARect 和 AText 这两个结构体是什么,这两个结构体实际上就是告诉 Draw 函数需要绘制的内容 59 | 60 | ```C++ 61 | 62 | ARect(100, 100, 200, 200) // 创建一个矩形,矩形默认出现在 (100, 100) 的右下方, 矩形的宽高为 (200, 200) 63 | ARect(100, 100, 200, 200, APos::LEFT_TOP) // 创建一个矩形,矩形出现在 (100, 100) 的左上方, 矩形的宽高为 (200, 200) 64 | 65 | AText("hello", 100, 100) // 创建一个字符串, 内容为 "hello", 字符串默认出现在 (100, 100) 的右下方,并且字符串默认有背景 66 | AText("hello", 100, 100, APos::LEFT_BOTTOM, false) // 创建一个字符串, 内容为 "hello", 字符串出现在 (100, 100) 的左下方,并且字符串没有背景 67 | ``` 68 | 69 | 在了解了这两个结构体之后,我相信 Draw 的第一个参数是什么意思了,那么 Draw 是有第二个参数的,第二个参数是绘制持续时间,首先 Draw 函数有一个默认的绘制时间,就是持续 1cs,还记得咱们的 hello AsmVsZombies 程序吗, 70 | 71 | ```C++ 72 | #include // 包含本框架的头文件 73 | 74 | // 本框架的入口函数是 `void AScript()` 75 | void AScript() 76 | { 77 | // 注意此条代码需要到选卡界面或者战斗界面才能看到运行效果 78 | aPainter.Draw(AText("Hello AsmVsZombies", 400, 300), 500); 79 | } 80 | ``` 81 | 82 | 现在我相信你已经理解了这条代码的真正含义,是的!他持续了 500cs。 83 | 84 | OK, 有了以上的介绍,我们就开始正式介绍这个玩意有啥用吧,首先,咱们可以用它显示一些特定植物的内存信息 85 | 对了,你可能疑惑这个 aPainter 咋就突然冒出来,实际上这是一个本框架预定义好的 `APainter` 类对象, 86 | 如果你想整好几个 APainter, 那你就使用 `APainter` 创建多个就行。 87 | 88 | ```C++ 89 | #include 90 | 91 | void DrawCobHp() 92 | { 93 | for (auto&& plant : aAlivePlantFilter) { 94 | if (plant.Type() == AYMJNP_47) { 95 | // 注意,AText 的第一个参数只接受字符串类型,plant.Hp() 返回值是一个整型 96 | // 所以不能直接使用,需要使用 std::to_string 函数将其转为字符串类型才能绘制 97 | aPainter.Draw(AText("春哥 Hp : " + std::to_string(plant.Hp()), 98 | plant.Abscissa(), plant.Ordinate())); 99 | } 100 | } 101 | } 102 | 103 | ATickRunner tickRunner; 104 | 105 | void AScript() 106 | { 107 | AConnect(ATime(1, -599), [] { 108 | tickRunner.Start(DrawCobHp); 109 | }); 110 | } 111 | ``` 112 | 113 | 上述的代码非常简单,首先读内存看一下这个植物是不是炮,如果是炮,就在他的位置处绘制一个字符串,字符串的内容就是他的血量,对就是这么简单,那么我们看一下运行结果 114 | 115 | ![painter](./img/painter.jpg) 116 | 117 | 118 | [目录](./0catalogue.md) -------------------------------------------------------------------------------- /tutorial/24logger.md: -------------------------------------------------------------------------------- 1 | 7 | # 日志功能 8 | 9 | 没想到吧,本框架是有日志功能的,咱们现在就开始介绍。 10 | 11 | 首先,本框架的日志功能由 `ALogger` 类实现的,他有以下四种创建方式。 12 | 13 | ```C++ 14 | #include 15 | ALogger msgBoxLogger; // 这个日志对象呈现日志的方式是弹出窗口 16 | ALogger consoleLogger; // 这个日志对象呈现日志的方式是控制台 17 | ALogger fileLogger("logger.txt"); // 这个日志对象呈现日志的方式是输出到文件 logger.txt 中 18 | ALogger pvzGuiLogger; // 这个日志对象呈现日志的方式是在 PvZ 界面中显示 19 | 20 | void AScript() 21 | { 22 | msgBoxLogger.Info("hello"); 23 | consoleLogger.Info("hello"); 24 | fileLogger.Info("hello"); // 注意由于只写了相对路径,所以它默认将 logger.txt 保存在 pvz 可执行程序路径下 25 | pvzGuiLogger.Info("hello"); 26 | } 27 | ``` 28 | 29 | 咱们看看上述代码的运行结果 30 | 31 | msgBoxLogger 32 | 33 | ![msbBox](./img/msgBox.jpg) 34 | 35 | consoleLogger 36 | 37 | ![console](./img/console.jpg) 38 | 39 | fileLogger 40 | 41 | ![file](./img/file.jpg) 42 | 43 | pvzGuiLogger 44 | 45 | ![pvzGui](./img/pvzGui.jpg) 46 | 47 | 你可能会有疑惑,这个 [1, -2147483648] 是个什么玩意,实际上这指的是游戏当前时间,但是由于咱们是在选卡界面显示的日志, 48 | 所以自然就没有这个时间,因为只有战斗界面才有时间,因此本框架就返回了一个这样的值,后面的中括号的内容显示的是显示级别 49 | 50 | ```C++ 51 | // 本框架分为以下四个日志级别 52 | ALogLevel::DEBUG; 53 | ALogLevel::INFO; 54 | ALogLevel::WARNING; 55 | ALogLevel::ERROR; 56 | ``` 57 | 58 | 下面介绍一下如何设置打印级别 59 | 60 | ```C++ 61 | msgBoxLogger.SetLevel({ALogLevel::DEBUG, ALogLevel::WARNING}); 62 | msgBoxLogger.Debug("Debug"); // 显示 63 | msgBoxLogger.Info("Info"); // 不显示 64 | msgBoxLogger.Warning("Warning"); // 显示 65 | msgBoxLogger.Error("Error"); // 不显示 66 | ``` 67 | 68 | 由于上述代码 msgBoxLogger 设置显示级别为 ALogLevel::DEBUG, ALogLevel::WARNING, 所以只有 Debug 和 Warning 级别的日志才会输出显示,别的级别的都不会进行输出。 69 | 70 | 接着咱们介绍一下如何设置日志对象的输入格式 71 | 72 | ```C++ 73 | // 参数格式与 C++20 的 std::format 相同 74 | consoleLogger.Info("时间是:{}", 5); // 输出结果: [1, -2147483648][INFO] 时间是:5 75 | consoleLogger.Info("成功率:{:.2f}%", 100.0/3); // 输出结果: [1, -2147483648][INFO] 成功率:33.33% 76 | 77 | // 设置日志头部格式 78 | // 头部时间格式有四个变量,分别是 flag, wave, time, level 79 | consoleLogger.SetHeaderStyle("[{level}] ({flag}f, {wave}, {time})"); 80 | consoleLogger.Info("时间是:{}", 5); // 输出结果是: [INFO] (2000f, 1, -2147483648) 时间是:5 81 | 82 | // 将头部设置为空 83 | consoleLogger.SetHeaderStyle(""); 84 | consoleLogger.Info("时间是:{}", 5); // 输出结果是: 时间是:5 85 | ``` 86 | 87 | ## 设置框架内部日志对象 88 | 89 | 在框架的使用过程中,你应该能看出来,本框架默认用的日志类是 `ALogger`,为什么用它呢,是因为他的报错提示最明显,而且框架默认将他的日志级别设置为 ALogLevel::WARNING 和 ALogLevel::ERROR,但是你可能觉得很不好,你可能想用一个控制台看框架内部的输出内容,那么你需要这样做 90 | 91 | ```C++ 92 | #include 93 | ALogger consoleLogger; // 这个日志对象呈现日志的方式是控制台 94 | 95 | void AScript() 96 | { 97 | // 将框架内部 INFO WARNING ERROR 等级的日志全部输出 98 | consoleLogger.SetLevel({ALogLevel::INFO, ALogLevel::WARNING, ALogLevel::ERROR}); 99 | // 设置框架内部日志对象为自己想要的对象 100 | ASetInternalLogger(consoleLogger); 101 | } 102 | ``` 103 | ## 自定义日志类 104 | 105 | 看到这,你可能还会有别的想法,我能不能实现这样一种日志对象,他在 INFO 的时候用控制台,DEBUG 的时候用文件,ERROR 的时候用 msgBox 呢?很明显这样的需求本框架没有封装,所以你需要自己写一个,大致写法如下 106 | 107 | ```C++ 108 | #include 109 | 110 | ALogger msgBoxLogger; // 这个日志对象呈现日志的方式是弹出窗口 111 | ALogger consoleLogger; // 这个日志对象呈现日志的方式是控制台 112 | ALogger fileLogger("logger.txt"); // 这个日志对象呈现日志的方式是输出到文件 logger.txt 中 113 | ALogger pvzGuiLogger; // 这个日志对象呈现日志的方式是在 PvZ 界面中显示 114 | 115 | class MyLogger : public AAbstractLogger { 116 | 117 | protected: 118 | // 重写日志抽象基类的虚函数 119 | virtual void _Output(ALogLevel level, std::string&& str) override 120 | { 121 | if (level == ALogLevel::INFO) { 122 | consoleLogger.Info(str); 123 | } else if (level == ALogLevel::DEBUG) { 124 | fileLogger.Debug(str); 125 | } else if (level == ALogLevel::ERROR) { 126 | fileLogger.Error(str); 127 | } // 这里就不写 WARNING 的了,不写会导致 WARNING 没有输出 128 | } 129 | }; 130 | 131 | MyLogger logger; 132 | void AScript() 133 | { 134 | logger.Info("hello"); // 输出 [1, -2147483648][INFO] [1, -2147483648][INFO] hello 135 | logger.Debug("hello"); 136 | logger.Error("hello"); 137 | } 138 | ``` 139 | 140 | 此时你可能疑惑为啥他重复输出了两次日志头,实际上很简单,因为当你调用 Info 函数的时候,他会先为字符串加上一个日志头, 141 | 然后日志头经过格式化处理再给咱们重载的 _Output 函数,因此 _Output 函数此时接收到的 std::string&& str 中就含有了文件头, 142 | 然后此时又调用了相应的日志对象的函数,此时又会加上一个日志头,因此就输出了两次,解决这个问题的办法就是 143 | 144 | ```C++ 145 | logger.SetHeaderStyle("") 146 | ``` 147 | 148 | OK,我相信你已经看懂了代码了。就不再解释了 149 | 150 | [目录](./0catalogue.md) -------------------------------------------------------------------------------- /tutorial/dsl/0 前言.md: -------------------------------------------------------------------------------- 1 | # 0 前言 2 | 3 | > 使用 DSL 最明显的优点在于,一旦您获得了一种语言和转换引擎,您在 DSL 覆盖的软件开发特定方面的工作就会变得更有**效率**,因为您不必手动完成繁琐的工作。\ 4 | 如果您有一种方法可以用与领域高度一致的语言表达领域重点,您的**思路将变得更加清晰**,因为您编写的代码不会被实现细节搞得混乱。 换言之,使用 DSL 允许您将基本点与复杂性分开。\ 5 | DSL 的领域、抽象和符号与领域专家(即非编程人员)如何表达自己高度一致,这就**在技术人员与领域人员之间形成良好的配合**。\ 6 | —— [JetBrains](https://www.jetbrains.com/zh-cn/mps/concepts/domain-specific-languages/) 7 | 8 | AvZ DSL 是建立在 AvZ2 基础上的新型脚本编写方式。DSL 与传统脚本相比更加贴近轨道语言,这不但能提高编写脚本的效率,也让阅读脚本更加方便。 9 | 10 | 为方便理解,推荐本教程的读者提前阅读 [AvZ2 本体教程](https://gitlab.com/vector-wlc/AsmVsZombies/-/blob/master/tutorial/0catalogue.md)的前 12 节。 11 | 12 | 想要使用 DSL,只需在脚本开头添加: 13 | 14 | ```cpp 15 | #include 16 | ``` 17 | 18 | [目录](../0catalogue.md) 19 | 20 | -------------------------------------------------------------------------------- /tutorial/dsl/1 时间轴是什么.md: -------------------------------------------------------------------------------- 1 | # 1 时间轴是什么 2 | 3 | 时间轴是 DSL 中的基本组件。说起时间轴,你可能会想到类似这样的图片: 4 | 5 | ![](../img/timeline.png) 6 | 7 | AvZ 中的时间轴(`ATimeline`)也与之类似。具体而言,时间轴是一系列操作的集合,每个操作对应一个**相对的**生效时间。什么叫相对时间呢?先举一个例子: 8 | 9 | ```cpp 10 | ATimeline a = At(-100_cs) Card(ACHERRY_BOMB, 2, 9); 11 | ``` 12 | 13 | 我们暂且不考虑其中的语法细节。可以看出,这行代码的两个关键元素是 `-100_cs` 和 `Card(...)`。前者是相对时间,后者是实际执行的操作。 14 | 15 | 这时有人可能要问了:定义它的过程中根本没有指定波次,那这个操作被连接到了哪波的 -100cs 啊?答案是:这行代码并没有把操作连接到任何地方。**时间轴需要连接到绝对时间才能生效。**就像这样: 16 | 17 | ```cpp 18 | AConnect(ATime(1, 359), a); 19 | ``` 20 | 21 | 在连接一个时间轴时,AvZ 会把每项操作的相对时间(此例中是 -100cs)加到连接的绝对时间(此例中是 (1, 359))上,以计算出这个操作的实际执行时机。因此,这个樱桃会在第 1 波的 260cs 时放置。 22 | 23 | 此外,两个时间轴还可以合并: 24 | 25 | ```cpp 26 | ATimeline p = At(-373_cs) Do { aCobManager.Fire(2, 9); }; 27 | ATimeline a = At(-100_cs) Do { ACard(ACHERRY_BOMB, 5, 9); }; 28 | AConnect(ATime(1, 401), p & a); 29 | ``` 30 | 31 | 这相当于: 32 | 33 | ```cpp 34 | AConnect(ATime(1, 401 - 373), []{ aCobManager.Fire(2, 9); }); 35 | AConnect(ATime(1, 401 - 100), []{ ACard(ACHERRY_BOMB, 5, 9); }); 36 | ``` 37 | 38 | 注意到一件事:`ATimeline` 版本的代码中,`401` 是写在 `AConnect` 里的,而 `-373` 和 `-100` 是写在两个时间轴的定义处的。为什么要作这样的区分?都写在一处不好吗? 39 | 40 | 想想你在内心思考阵解时,是不是先想着“我要让炮在 401cs 生效”,然后再在脚本里写下 `401 - 373` 这样的数值?对于各种操作,我们往往更加关心其**生效时间**而非**操作时间**。把 `-373_cs` 绑定到发炮操作上就起到了把后者转换为前者的效果。你只需要在 `AConnect` 里写下预期的生效时间,AvZ 内部就会自动计算好对应的操作时间。是不是很方便? 41 | 42 | 更重要的一点是:这些时间轴操作可以被**预先封装**。使用 DSL 自带的封装好的时间轴,上一个例子可以写成: 43 | 44 | ```cpp 45 | AConnect(ATime(1, 401), P(2, 9) & A(5, 9)); 46 | ``` 47 | 48 | 这就是时间轴语法的最大优势:它可以通过封装避免脚本中避免大量的时间加减,让脚本更加易写、易读。 49 | 50 | 相信读者已经理解了 DSL 存在的意义。后面的章节将会逐一介绍 DSL 的语法,并一步步地构建完整的 DSL 脚本。 51 | 52 | [目录](../0catalogue.md) 53 | -------------------------------------------------------------------------------- /tutorial/dsl/2 构建时间轴.md: -------------------------------------------------------------------------------- 1 | # 2 构建时间轴 2 | 3 | ## 2.1 把 AvZ 自带的操作转换为时间轴 4 | 5 | AvZ 自带的操作(如 `ACard`、`ACobManager::Fire` 等)都是**立即生效**的。也就是说: 6 | 7 | ```cpp 8 | // 不能这么写 9 | AConnect(ATime(1, -599), AMaidCheats::Dancing()); 10 | // 而应该 11 | AConnect(ATime(1, -599), []{ AMaidCheats::Dancing(); }); 12 | ``` 13 | 14 | DSL 的情况与此类似。想把一个立即生效的操作转换为时间轴,只需把操作包裹在 `Do { ... }` 里。`Do` 本质上是在创建一个闭包,类似于原生 AvZ 脚本中的 `[=]{ }`。 15 | 16 | ```cpp 17 | ATimeline ice = Do { ACard(AICE_SHROOM, 1, 1); }; // 注意:里面和外面都需要分号 18 | ATimeline jw = CoDo { // 大括号里面使用协程时,需要把 Do 换成 CoDo 19 | aCobManager.Fire({{2, 9}, {5, 9}}); 20 | co_await 107; 21 | aCobManager.Fire({{1, 7.8125}, {5, 7.8125}}); 22 | }; 23 | ``` 24 | 25 | ## 2.2 DSL 提供的时间轴 26 | 27 | DSL 预先定义了大量返回时间轴的函数。为方便使用,这些函数的名称普遍非常短。如果对这些名称不满意,请参阅[附录 A](A%20关于%20shorthand.h.md)。 28 | 29 | 这些函数返回的时间轴都**以生效时间,而非操作时间为基准**(这两者的含义在上一章中已经说明),也就是说使用者无需添加 `-373`、`-100` 之类的偏移。 30 | 31 |
32 | 完整的列表 33 | 34 | ```cpp 35 | P(1, 8.75) // 炮炸 1-8.75 36 | P(1256, 9) // 炮炸 1-9、2-9、5-9、6-9 37 | P(1256, 9) // 等待炮恢复后炸 1-9、2-9、5-9、6-9(RECOVER_FIRE 只对 P 和 PP 有效) 38 | PP(15, 9, 26, 8) // 炮炸 1-9、5-9、2-8、6-8 39 | P(slope, 2, 9) & P(flat, 4, 9) // 风炮炸 2-9,平炮炸 4-9(slope 和 flat 是 DSL 提供的) 40 | P(slope, 2, 9, flat, 4, 9) // 同上 41 | PP(8) // 在五行场地炸 2-8、4-8;在六行场地炸 2-8、5-8 42 | PP() // 等效于 PP(9) 43 | D<110>(1, 8.75) // 等效于 At(110) P(1, 8.75)(注意 <> 里的数值必须是编译时常量,不能传一个变量的值) 44 | DD<110>(8.75) // 在六行场地炸 1、5 路;在五行场地炸 1、4 路 45 | 46 | Card(ASPIKEWEED, 1, 9) // 在 1-9 种地刺 47 | // Card 与 ACard 用法相同,但 Card 会自动补种荷叶和花盆 48 | Shovel(1, 9) // 铲 1-9 的普通植物 49 | Shovel(1, 9, APUMPKIN) // 铲 1-9 的南瓜(没有则不铲除) 50 | 51 | A(2, 9) // 在 2-9 使用樱桃(与 Card 相比附加了 -100cs 的偏移,相当于以生效时间为基准;下同) 52 | J(2, 9) // 使用辣椒 53 | a(2, 9) // 使用窝瓜 54 | N(2, 9) // 使用毁灭菇(自动使用咖啡豆,自动校正生效时间;下同) 55 | N(3, 9, true) // 优先使用模仿者卡片,其次使用原版卡片 56 | N({{3, 8}, {3, 9}, {4, 9}}) // 从位置列表中挑选第一个可用位置使用 57 | I(1, 1) // 优先使用模仿者卡片,其次使用原版卡片 58 | I(1, 1, false) // 只尝试使用原版卡片 59 | I() // 仅限白天:使用 aIceFiller 中的存冰 60 | 61 | C.SetCards({APUFF_SHROOM, AM_PUFF_SHROOM}); // 设置使用的垫材;不设置时默认为所有 751cs 冷却的植物按阳光从低到高排序(这个函数是即时生效的) 62 | C(134) // 在所有陆地行 9 列放置垫材,134cs 后铲除 63 | C(-1, 56) // 在 5、6 行 9 列放置垫材,不主动铲除 64 | C(1, {1256, 8}) // 在 1、2、5、6 行 8 列放置垫材,秒铲 65 | C(266, {{1, 9}, {256, 8}}) // 在 1-9、2-8、5-8、6-8 放置垫材,266cs 后铲除 66 | C.TriggerBy(ADANCING_ZOMBIE & CURR_WAVE, AGIGA_GARGANTUAR & PREV_WAVES)(40) // 只在有本波舞王或非本波红眼的行放置垫材 67 | C.TriggerBy(ALADDER_ZOMBIE, AJACK_IN_THE_BOX_ZOMBIE & XIn(600, 700))(266) // 只在有梯子或横坐标位于 600~700 之间的小丑的行放置垫材 68 | 69 | // Trig 是 trigger refresh 的简写 70 | Trig() // 起到假定波长的作用;注意这个函数应该被连接到激活时间,波长 = max(激活时间, 401) + 200 71 | Trig(true) // 锁定波长,用法同上 72 | ``` 73 |
74 | 75 | 不要被它的长度吓到:你不需要立刻把这些用法都背下来。你可以在写脚本时随时查阅。 76 | 77 | 78 | ## 2.3 组合时间轴 79 | 80 | 组合多个时间轴相当于把它们包含的操作合并起来。有两种方式组合时间轴:`&` 和 `{}`。 81 | 82 | ```cpp 83 | ATimeline a, b; 84 | 85 | ATimeline c = a & b; 86 | ATimeline d = { 87 | a, 88 | b, // 最后一行的逗号不是必需的,但建议加上,这样在添加内容时更方便 89 | }; // 注意:每个元素后面是逗号,大括号后面是分号 90 | ``` 91 | 92 | ## 2.4 给时间轴添加偏移 93 | 94 | 在组合多个时间轴时,我们往往希望不同的时间轴在不同的时间运行。想要在时间轴上附加一个时间差,需要使用 `At` 或 `+` 运算符。 95 | 96 | ```cpp 97 | ATimeline a, b; 98 | 99 | // AConnect(ATime(wave, time), c) 相当于 AConnect(ATime(wave, time + 100), a) 100 | ATimeline c = At(100_cs) a; 101 | ATimeline c2 = a + 100_cs; // 这两句是等效的;后面的 d 与 d2 等同理 102 | // 大括号也可以用在这里 103 | ATimeline d = At(-100_cs) { 104 | a, 105 | b, 106 | c, 107 | }; 108 | ATimeline d2 = (a & b & c) - 100_cs; 109 | 110 | ATimeline e = At(1) At(-1) d; // 后缀 _cs 通常可以省略;多个 At 可以组合 111 | 112 | ATimeline f = At(100_cs) a & b; // At 的优先级低于 &;这个表达式等同于 At(100_cs) (a & b) 113 | ATimeline g = a & (At(100_cs) b); // 想要先计算 At 需要人为添加括号 114 | ATimeline g2 = a & b + 100_cs; // + 的优先级高于 & 115 | 116 | // 组合多个带 At 的组件时,使用大括号组合更加清楚易懂 117 | ATimeline pdc = { 118 | At(360_cs) P(2, 9) & A(5, 9) & (DD(7.8125) + 107_cs), 119 | At(361_cs) C(300), 120 | At(401_cs) Do { AMaidCheats::Dancing(); }, 121 | }; 122 | ``` 123 | 124 | [目录](../0catalogue.md) 125 | -------------------------------------------------------------------------------- /tutorial/dsl/3 连接时间轴.md: -------------------------------------------------------------------------------- 1 | # 3 连接时间轴 2 | 3 | 前面曾强调过,**定义时间轴并不意味着执行操作**(如果你忘了,编译器也会用报错告诉你)。想要执行操作,需要把时间轴绑定到绝对时间。绑定的过程和构建时间轴使用同一套语法:把绝对时间填入 `At` 的小括号里,后面跟着要绑定的操作即可。 4 | 5 | ```cpp 6 | // 1_wave 就是一个绝对时间,相当于 ATime(1, 0) 7 | At(1_wave) { 8 | At(401_cs) PP(), 9 | }; 10 | // 相当于 11 | AConnect(ATime(1, 401), PP()); 12 | 13 | // 绝对时间只能有一个,而且必须在最外层 14 | // At(401_cs) At(1_wave) PP(); 是不行的 15 | 16 | At(ATime(2, 1 - 100)) Do { 17 | // Do 里面是一个程序块,块里的内容会在 ATime(2, 1 - 100) 这个时间点运行 18 | // 和在 AScript() 里一样,想使用时间轴操作仍然需要指定一个绝对时间。At(now) 就是现在执行的意思 19 | At(now) Card(AICE_SHROOM, 1, 1); 20 | At(now + 100_cs) Do { logger.Info("冰生效"); }; 21 | }; // 注意结尾分号 22 | // 相当于 23 | AConnect(ATime(2, 1 - 100), []{ 24 | AConnect(ANowTime(), Card(AICE_SHROOM, 1, 1)); 25 | AConnect(ANowDelayTime(100), []{ logger.Info("冰生效"); }); 26 | }); 27 | 28 | At(ATime(2, 1)) Do { 29 | At(now) I(1, 1); // 这样写是错的! 30 | // I(1, 1) 以生效时间为基准,需要提前 100cs 放冰,但它被连接到了 now,now - 100_cs 已经过去了 31 | // 这就相当于你在看到第二波僵尸出来之后才想起“这波应该预判冰”,肯定是不行的 32 | logger.Info("冰生效"); 33 | }; 34 | // 应该 35 | At(ATime(2, 1)) { 36 | I(1, 1), // 这种写法中操作会在执行脚本时(如果你没有阻塞脚本的话,就是战斗开始的瞬间)被绑定,这时 ATime(2, 1) - 100_cs 还尚未到达 37 | Do { logger.Info("冰生效"); }, 38 | }; 39 | ``` 40 | 41 | DSL 还支持把同样的操作连接到多波。 42 | 43 | ```cpp 44 | // 把操作在第 1-3 波每波各执行一次 45 | At(1_3_wave) { 46 | ... 47 | }; 48 | At(10_19_step3_wave) // 第 10、13、16、19 波 49 | At(1_8_wave, 10_18_wave, 20_wave) // 第 1-8、10-18、20 波 50 | At(AWave(1_8, 10_18, 20)) // 同上 51 | OnWave(1_8, 10_18, 20) // OnWave(...) 是 At(AWave(...)) 的简写 52 | ``` 53 | 54 | 此外,还有一种特殊的连接语法:假设 `a`、`b`、`c` 是三个时间轴,则可以通过 `a | b | c` 创建一个**轨道**(类型名为 `ASequence`)。轨道是无限循环往复的一列时间轴,当轨道被连接到多个波次时,各个波次会**按顺序逐个连接轨道中的时间轴**。举个例子: 55 | 56 | ```cpp 57 | OnWave(1_3, 5_9) a | b | c; 58 | /* 59 | 波次 1 2 3 5 6 7 8 9 60 | 操作 a b c a b c a b 61 | 注意:第 5 波的操作仍然是第 3 波的后继,中间跳过的波次不会影响轨道的循环 62 | */ 63 | 64 | // 相当于 65 | OnWave(1, 5, 8) a; 66 | OnWave(2, 6, 9) b; 67 | OnWave(3, 7) c; 68 | ``` 69 | 70 | 但是,上面的例子还有一个不足:如果第一波想从 `b` 开始怎么办呢?如果想空出第 4 波,让第 5 波的相位和第 2 波一样呢?这时我们需要 **人工指定相位**。 71 | 72 | ```cpp 73 | // % 后的数字指定了起始相位(从 1 开始计数) 74 | OnWave(1_3 % 2, 5_9 % 3) a | b | c; 75 | /* 76 | 相位是一个递增的计数器,% 运算符会重置其值(比如 5_9_wave % 3 就会把第 5 波的相位赋值为 3,后面在此基础上继续递增) 77 | 波次 1 2 3 5 6 7 8 9 78 | 相位 2 3 4 3 4 5 6 7 79 | 操作 b c a c a b c a 80 | */ 81 | ``` 82 | 83 | [目录](../0catalogue.md) 84 | 85 | -------------------------------------------------------------------------------- /tutorial/dsl/4 脚本示例:[FE] 经典四炮.md: -------------------------------------------------------------------------------- 1 | # 4 脚本示例:\[FE\] 经典四炮 2 | 3 | ```cpp 4 | // 布阵码:LI43NMQIfe3BVVEc+sHIXUAPX7dvSCBUt1TS1OE8dn9XgraGkdQ1ZlSKiEUaVw== 5 | // 节奏:邻C6u I-PP|I-PP|N|PP 1976|1976|750|749 6 | 7 | #include 8 | #include 9 | 10 | ALogger logger; 11 | 12 | void AScript() { 13 | // 对这一部分不熟悉的话建议回顾 AvZ 本体教程 14 | ASetInternalLogger(logger); 15 | ASetReloadMode(AReloadMode::MAIN_UI); 16 | ASetZombies("普杆舞车豚 丑气矿篮偷 红"); 17 | ASelectCards("IINAW LPccc"); 18 | aPlantFixer.Start(APUMPKIN, {}, 4000 / 3); 19 | 20 | // 定义用冰和用核操作(随用随定义也可以,但这么写在需要修改冰位核位时只需要改一处) 21 | ATimeline ice = I(3, 5), doom = N({{3, 8}, {3, 9}, {4, 9}}); 22 | 23 | // 冰波:I-PP 1976 24 | ATimeline i = {At(11_cs) ice, TrigAt(1776_cs) PP(8.75)}; 25 | // 加速波 1:N 750 26 | ATimeline n = {TrigAt(550_cs) doom}; 27 | // 加速波 2:PP 749 28 | ATimeline p = {TrigAt(549_cs) PP(8.75)}; 29 | 30 | // 主循环:第 1 波首代,第 2 波开始循环 31 | // 注意 1_1 % 4 不能写成 1 % 4,后者会被当作算术运算 32 | OnWave(1_1 % 4, 2_9 % 2, 11_19 % 1) i | i | n | p; 33 | 34 | // 第 10 波 PPAa 消延迟 35 | OnWave(10) { 36 | TrigAt(341_cs) PP(), 37 | At(401_cs) A(2, 9) & W(5, 9), 38 | }; 39 | 40 | // 第 9、19 波额外补一波操作 41 | OnWave(9, 19) At(1976_cs) i; 42 | 43 | OnWave(20) { 44 | At(341_cs) PP(), 45 | At(395_cs) ice, 46 | At(2000_cs) PP(), 47 | }; 48 | } 49 | ``` 50 | 51 | [目录](../0catalogue.md) 52 | 53 | -------------------------------------------------------------------------------- /tutorial/dsl/A 关于 shorthand.h.md: -------------------------------------------------------------------------------- 1 | # 关于 `shorthand.h` 2 | 3 | 与 AvZ 本体不同,`shorthand.h` **不保证向后兼容**。为保证更新之后已有的脚本仍然可以运行,AvZ 存储库会保留 `shorthand.h` 的所有版本,路径为 `inc/dsl/shorthand_xxxxxx.h`。`shorthand.h` 永远指向最新的版本。在对外发布脚本时,最好把 `#include ` 改为 `#include `,方便使用者运行。 4 | 5 | 有些人可能不喜欢 `shorthand.h` 中的一些名称,或者是想改进其中的功能。由于 AvZ 的更新机制,**对 AvZ 提供的文件作出的修改会在下次更新时被覆盖**。如果对 `shorthand.h` 不满意,有以下几种解决方案: 6 | 7 | ## 1 不使用 `shorthand.h` 8 | 9 | 把脚本中的 `#include ` 改为 `#include ` 即可。你可能还需要 `using namespace ALiterals;` 以使用 `_wave`、`_cs` 等。 10 | 11 | 不建议使用这种方法。 12 | 13 | ## 2 创建 `shorthand.h` 的副本 14 | 15 | 复制 `inc/dsl/shorthand_[最新版本].h`,并粘贴到同一目录下,新文件随便取一个名字(比如 `my_shorthand.h`)。在新文件里进行你想要的修改,使用时把 `#include ` 改为 `#include ` 即可。 16 | 17 | 18 | [目录](../0catalogue.md) 19 | 20 | -------------------------------------------------------------------------------- /tutorial/img/ImDisk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/img/ImDisk.jpg -------------------------------------------------------------------------------- /tutorial/img/ImDisk1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/img/ImDisk1.jpg -------------------------------------------------------------------------------- /tutorial/img/ImDisk2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/img/ImDisk2.jpg -------------------------------------------------------------------------------- /tutorial/img/RE10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/img/RE10.jpg -------------------------------------------------------------------------------- /tutorial/img/RE18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/img/RE18.jpg -------------------------------------------------------------------------------- /tutorial/img/console.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/img/console.jpg -------------------------------------------------------------------------------- /tutorial/img/file.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/img/file.jpg -------------------------------------------------------------------------------- /tutorial/img/hello_avz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/img/hello_avz.jpg -------------------------------------------------------------------------------- /tutorial/img/msgBox.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/img/msgBox.jpg -------------------------------------------------------------------------------- /tutorial/img/painter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/img/painter.jpg -------------------------------------------------------------------------------- /tutorial/img/pvzGui.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/img/pvzGui.jpg -------------------------------------------------------------------------------- /tutorial/img/timeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/img/timeline.png -------------------------------------------------------------------------------- /tutorial/scripts/4th_anniversary_celebration/alterit__fe_97_no_cob/game1_14.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/scripts/4th_anniversary_celebration/alterit__fe_97_no_cob/game1_14.dat -------------------------------------------------------------------------------- /tutorial/scripts/4th_anniversary_celebration/artificialabyss__re_10/game1_15.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/scripts/4th_anniversary_celebration/artificialabyss__re_10/game1_15.dat -------------------------------------------------------------------------------- /tutorial/scripts/4th_anniversary_celebration/pokey8__fe_10/game1_13.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/scripts/4th_anniversary_celebration/pokey8__fe_10/game1_13.dat -------------------------------------------------------------------------------- /tutorial/scripts/4th_anniversary_celebration/reisen__fe_24/game1_14.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/scripts/4th_anniversary_celebration/reisen__fe_24/game1_14.dat -------------------------------------------------------------------------------- /tutorial/scripts/4th_anniversary_celebration/shallow_dream__pe_alternative_16/game1_13.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/scripts/4th_anniversary_celebration/shallow_dream__pe_alternative_16/game1_13.dat -------------------------------------------------------------------------------- /tutorial/scripts/dsl_jing_dian_4/game1_14.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/scripts/dsl_jing_dian_4/game1_14.dat -------------------------------------------------------------------------------- /tutorial/scripts/dsl_jing_dian_4/jing_dian_4.cpp: -------------------------------------------------------------------------------- 1 | // 布阵码:LI43NMQIfe3BVVEc+sHIXUAPX7dvSCBUt1TS1OE8dn9XgraGkdQ1ZlSKiEUaVw== 2 | // 节奏:邻C6u I-PP|I-PP|N|PP 1976|1976|750|749 3 | 4 | #include 5 | #include 6 | 7 | ALogger logger; 8 | 9 | void AScript() { 10 | // 对这一部分不熟悉的话建议回顾 AvZ 本体教程 11 | ASetInternalLogger(logger); 12 | ASetReloadMode(AReloadMode::MAIN_UI); 13 | ASetZombies("普杆舞车豚 丑气矿篮偷 红"); 14 | ASelectCards("IINAW LPccc"); 15 | aPlantFixer.Start(APUMPKIN, {}, 4000 / 3); 16 | 17 | // 定义用冰和用核操作(随用随定义也可以,但这么写在需要修改冰位核位时只需要改一处) 18 | ATimeline ice = I(3, 5), doom = N({{3, 8}, {3, 9}, {4, 9}}); 19 | 20 | // 冰波:I-PP 1976 21 | ATimeline i = {At(11_cs) ice, TrigAt(1776_cs) PP(8.75)}; 22 | // 加速波 1:N 750 23 | ATimeline n = {TrigAt(550_cs) doom}; 24 | // 加速波 2:PP 749 25 | ATimeline p = {TrigAt(549_cs) PP(8.75)}; 26 | 27 | // 主循环:第 1 波首代,第 2 波开始循环 28 | // 注意 1_1 % 4 不能写成 1 % 4,后者会被当作算术运算 29 | OnWave(1_1 % 4, 2_9 % 2, 11_19 % 1) i | i | n | p; 30 | 31 | // 第 10 波 PPAa 消延迟 32 | OnWave(10) { 33 | TrigAt(341_cs) PP(), 34 | At(401_cs) A(2, 9) & W(5, 9), 35 | }; 36 | 37 | // 第 9、19 波额外补一波操作 38 | OnWave(9, 19) At(1976_cs) i; 39 | 40 | OnWave(20) { 41 | At(341_cs) PP(), 42 | At(395_cs) ice, 43 | At(2000_cs) PP(), 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /tutorial/scripts/jing_dian_12/game1_13.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/scripts/jing_dian_12/game1_13.dat -------------------------------------------------------------------------------- /tutorial/scripts/jing_dian_12/jing_dian_12_co_await.cpp: -------------------------------------------------------------------------------- 1 | // 这里使用经典十二炮作为第一个键控脚本,经典十二炮可以说是最简单的键控脚本了,没有之一 2 | 3 | // **请强烈注意,我不是键控技术党,我是键控工具党,所以示例脚本的目的是教会大家如何使用本框架的接口,** 4 | // **脚本中的一些数值可能并不是科学的数值,科学的数值请询问当前植吧顶尖技术党或者查看相关的帖子和B站动态等,但由于本人早已不是键控技术党,** 5 | // **而且没有精力和兴趣去学相关的键控技术知识。** 6 | 7 | // **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 8 | 9 | // **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 10 | 11 | // **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 12 | 13 | #include 14 | 15 | ACoroutine ACoScript() 16 | { 17 | ASetZombies({ 18 | ACG_3, // 撑杆 19 | ATT_4, // 铁桶 20 | ABC_12, // 冰车 21 | AXC_15, // 小丑 22 | AQQ_16, // 气球 23 | AFT_21, // 扶梯 24 | ATL_22, // 投篮 25 | ABY_23, // 白眼 26 | AHY_32, // 红眼 27 | ATT_18, // 跳跳 28 | }); 29 | ASelectCards({ 30 | AICE_SHROOM, // 寒冰菇 31 | AM_ICE_SHROOM, // 模仿寒冰菇 32 | ACOFFEE_BEAN, // 咖啡豆 33 | ADOOM_SHROOM, // 毁灭菇 34 | ALILY_PAD, // 荷叶 35 | ASQUASH, // 倭瓜 36 | ACHERRY_BOMB, // 樱桃炸弹 37 | ABLOVER, // 三叶草 38 | APUMPKIN, // 南瓜头 39 | APUFF_SHROOM, // 小喷菇 40 | }); 41 | 42 | co_await ATime(1, -599); 43 | aCobManager.AutoSetList(); 44 | 45 | for (int wave = 1; wave < 21; ++wave) { 46 | if (wave == 10) { 47 | // wave 10 的附加操作 48 | // 樱桃消延迟 49 | co_await ATime(wave, 341 - 373); 50 | aCobManager.Fire({{2, 9}, {5, 9}}); 51 | co_await ATime(wave, 341 - 100); 52 | ACard(ACHERRY_BOMB, 2, 9); 53 | } else if (wave == 20) { 54 | // wave 20 的附加操作 55 | // 咆哮珊瑚(炮消) 56 | co_await ATime(wave, 250 - 378); 57 | aCobManager.Fire(4, 7.625); 58 | co_await ATime(wave, 341 - 373); 59 | aCobManager.Fire({{2, 9}, {5, 9}}); 60 | // wave 20 的附加操作 61 | // 收尾发四门炮 62 | co_await ATime(wave, 300); 63 | aCobManager.RecoverFire({{2, 9}, {5, 9}, {2, 9}, {5, 9}}); 64 | } else { 65 | // P6 66 | // 主体节奏 67 | co_await ATime(wave, 341 - 373); 68 | aCobManager.Fire({{2, 9}, {5, 9}}); 69 | 70 | // wave 9 19 的附加操作 71 | // 收尾发四门炮 72 | if (wave == 19 || wave == 9) { 73 | co_await ATime(wave, 300); 74 | aCobManager.RecoverFire({{2, 9}, {5, 9}, {2, 9}, {5, 9}}); 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /tutorial/scripts/jing_dian_12/jing_dian_12_connect.cpp: -------------------------------------------------------------------------------- 1 | // 这里使用经典十二炮作为第一个键控脚本,经典十二炮可以说是最简单的键控脚本了,没有之一 2 | 3 | // **请强烈注意,我不是键控技术党,我是键控工具党,所以示例脚本的目的是教会大家如何使用本框架的接口,** 4 | // **脚本中的一些数值可能并不是科学的数值,科学的数值请询问当前植吧顶尖技术党或者查看相关的帖子和B站动态等,但由于本人早已不是键控技术党,** 5 | // **而且没有精力和兴趣去学相关的键控技术知识。** 6 | 7 | // **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 8 | 9 | // **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 10 | 11 | // **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 12 | 13 | #include 14 | 15 | void AScript() 16 | { 17 | ASetZombies({ 18 | ACG_3, // 撑杆 19 | ATT_4, // 铁桶 20 | ABC_12, // 冰车 21 | AXC_15, // 小丑 22 | AQQ_16, // 气球 23 | AFT_21, // 扶梯 24 | ATL_22, // 投篮 25 | ABY_23, // 白眼 26 | AHY_32, // 红眼 27 | ATT_18, // 跳跳 28 | }); 29 | ASelectCards({ 30 | AICE_SHROOM, // 寒冰菇 31 | AM_ICE_SHROOM, // 模仿寒冰菇 32 | ACOFFEE_BEAN, // 咖啡豆 33 | ADOOM_SHROOM, // 毁灭菇 34 | ALILY_PAD, // 荷叶 35 | ASQUASH, // 倭瓜 36 | ACHERRY_BOMB, // 樱桃炸弹 37 | ABLOVER, // 三叶草 38 | APUMPKIN, // 南瓜头 39 | APUFF_SHROOM, // 小喷菇 40 | }); 41 | 42 | AConnect(ATime(1, -599), [] { 43 | aCobManager.AutoSetList(); 44 | }); 45 | 46 | // P6 47 | // 主体节奏 48 | for (auto wave : {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19}) { 49 | AConnect(ATime(wave, 341 - 373), [] { 50 | aCobManager.Fire({{2, 9}, {5, 9}}); 51 | }); 52 | } 53 | 54 | // wave 9 19 20 的附加操作 55 | // 收尾发四门炮 56 | for (auto wave : {9, 19, 20}) { 57 | AConnect(ATime(wave, 300), [] { 58 | aCobManager.RecoverFire({{2, 9}, {5, 9}, {2, 9}, {5, 9}}); 59 | }); 60 | } 61 | 62 | // wave 10 的附加操作 63 | // 樱桃消延迟 64 | // 解决僵尸出生点靠右的问题 65 | AConnect(ATime(10, 341 - 100), [] { 66 | ACard(ACHERRY_BOMB, 2, 9); 67 | }); 68 | AConnect(ATime(10, 341 - 373), [] { 69 | aCobManager.Fire({{2, 9}, {5, 9}}); 70 | }); 71 | 72 | // wave 20 的附加操作 73 | // 咆哮珊瑚(炮消) 74 | // 解决僵尸出生点靠右的问题 75 | 76 | AConnect(ATime(20, 250 - 378), [] { 77 | aCobManager.Fire(4, 7.625); 78 | }); 79 | AConnect(ATime(20, 341 - 373), [] { 80 | aCobManager.Fire({{2, 9}, {5, 9}}); 81 | }); 82 | } -------------------------------------------------------------------------------- /tutorial/scripts/jing_dian_2/game1_13.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/scripts/jing_dian_2/game1_13.dat -------------------------------------------------------------------------------- /tutorial/scripts/jing_dian_4/game1_13.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/scripts/jing_dian_4/game1_13.dat -------------------------------------------------------------------------------- /tutorial/scripts/jing_dian_4/jing_dian_4.cpp: -------------------------------------------------------------------------------- 1 | 2 | // 这里使用经典四炮作为第二个键控脚本,经典四炮可以将我们刚才学习的那几个接口进行运用,这里不再提供阻塞版本 3 | 4 | // **请强烈注意,我不是键控技术党,我是键控工具党,所以示例脚本的目的是教会大家如何使用本框架的接口,** 5 | // **脚本中的一些数值并不是科学的数值,科学的数值请询问当前植吧顶尖技术党或者查看相关的帖子和B站动态等,但由于本人早已不是键控技术党,** 6 | // **而且没有精力和兴趣去学相关的键控技术知识。** 7 | 8 | // **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 9 | 10 | // **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 11 | 12 | // **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 13 | 14 | #include 15 | 16 | void AScript() 17 | { 18 | ASetGameSpeed(10); 19 | // 这种调用方式用于挂机,但是此脚本实际上不能挂机,还是需要一些手动操作 20 | // 注意由于第二次进入游戏会导致核无法正常种下 21 | // 所以 AAssumeWavelength 会报 N 个错误 22 | ASetReloadMode(AReloadMode::MAIN_UI_OR_FIGHT_UI); 23 | ASetZombies({APJ_0, ATT_4, AWW_8, AQS_11, ABC_12, AXC_15, AKG_17, AHT_14, AFT_21, ABY_23, AHY_32, ABJ_20}); 24 | ASelectCards({AICE_SHROOM, AM_ICE_SHROOM, ACOFFEE_BEAN, ADOOM_SHROOM, ALILY_PAD, ASQUASH, ACHERRY_BOMB, ABLOVER, APUMPKIN, APUFF_SHROOM}); 25 | AAssumeWavelength({ 26 | ATime(1, 601), 27 | ATime(2, 1800), 28 | ATime(3, 1800), 29 | ATime(4, 1150), 30 | ATime(5, 601), 31 | ATime(6, 1800), 32 | ATime(7, 1800), 33 | ATime(8, 1150), 34 | ATime(10, 601), 35 | ATime(11, 1800), 36 | ATime(12, 1800), 37 | ATime(13, 1150), 38 | ATime(14, 601), 39 | ATime(15, 1800), 40 | ATime(16, 1800), 41 | ATime(17, 1150), 42 | ATime(18, 601), 43 | }); 44 | 45 | AConnect(ATime(1, -599), [=] { 46 | aCobManager.SetList({{3, 1}, {4, 1}, {3, 3}, {4, 3}}); 47 | aIceFiller.Start({{3, 5}, {1, 4}, {6, 4}, {1, 5}, {6, 5}}); 48 | 49 | // 自动补南瓜 50 | aPlantFixer.Start(ANGT_30, {{3, 5}, {3, 6}, {4, 5}, {4, 6}, {1, 4}, {6, 4}}); 51 | aPlantFixer.SetHp(4000 / 3 * 2); 52 | }); 53 | 54 | for (auto wave : {1, 5, 9, 14, 18}) { 55 | AConnect(ATime(wave, 341 - 373), [=] { 56 | aCobManager.Fire({{2, 9}, {5, 9}}); 57 | }); 58 | if (wave == 9) { 59 | AConnect(ATime(wave, 341 - 373), [=] { 60 | aCobManager.RecoverFire({{2, 8.5}, {5, 8.5}}); 61 | }); 62 | } else { 63 | // 这就是 AAssumeWavelength 带来的好处,可以让时间点的书写小于 -200 64 | AConnect(ATime(wave + 1, -298 + 1) /* 1 是为了兼容栈位带来的 1cs 偏差*/, [=] { 65 | aIceFiller.Coffee(); 66 | }); 67 | } 68 | } 69 | 70 | for (auto wave : {2, 6, 11, 15, 19}) { 71 | AConnect(ATime(wave, 1800 - 373 - 200), [=] { 72 | aCobManager.Fire({{2, 8.5}, {5, 8.5}}); 73 | }); 74 | 75 | if (wave == 19) { 76 | AConnect(ATime(wave, 1800 - 373 - 200), [=] { 77 | aCobManager.RecoverFire({{2, 8.5}, {5, 8.5}}); 78 | }); 79 | 80 | // 停止自动存冰线程 81 | AConnect(ATime(wave, 1800 - 373 - 200 + 1000), [=] { 82 | aIceFiller.Pause(); 83 | }); 84 | 85 | } else { 86 | // Ice3 87 | AConnect(ATime(wave, 1800 - 298 - 200 + 211), [=] { 88 | aIceFiller.Coffee(); 89 | ASetPlantActiveTime(AICE_SHROOM, 298); 90 | }); 91 | } 92 | } 93 | 94 | for (auto wave : {3, 7, 12, 16}) { 95 | AConnect(ATime(wave, 1800 - 373 - 200), [=] { 96 | aCobManager.Fire({{2, 8.5}, {5, 8.5}}); 97 | }); 98 | // Ice3 99 | AConnect(ATime(wave, 1800 - 298 - 200 + 211), [=] { 100 | aIceFiller.Coffee(); 101 | ASetPlantActiveTime(AICE_SHROOM, 298); 102 | }); 103 | } 104 | 105 | for (auto wave : {4, 8, 13, 17}) { 106 | // 使用核 107 | // 注意这个核不一定会稳定刷新 108 | AConnect(ATime(wave, 1150 - 200 - 298), [=] { 109 | if (ARangeIn(wave, {4, 17})) { 110 | ACard({{ALILY_PAD, 3, 8}, {ADOOM_SHROOM, 3, 8}, {ACOFFEE_BEAN, 3, 8}}); 111 | } else if (wave == 8) { 112 | ACard({{ALILY_PAD, 3, 9}, 113 | {ADOOM_SHROOM, 3, 9}, 114 | {ACOFFEE_BEAN, 3, 9}, 115 | {APUMPKIN, 3, 9}}); 116 | } else { 117 | ACard({{ALILY_PAD, 4, 9}, 118 | {ADOOM_SHROOM, 4, 9}, 119 | {ACOFFEE_BEAN, 4, 9}, 120 | {APUMPKIN, 4, 9}}); 121 | } 122 | 123 | // 这里因为栈位问题会带来 1cs 的偏差 124 | // 由于使用了假定波长函数,这里的波长必须和假定的波长严格保持一致 125 | // 不然会报错,因此需要使用植物精准生效函数 126 | ASetPlantActiveTime(ADOOM_SHROOM, 298); 127 | }); 128 | } 129 | 130 | // wave 10 131 | AConnect(ATime(10, 341 - 373), [] { 132 | aCobManager.Fire({{2, 9}, {5, 9}}); 133 | }); 134 | 135 | // 种植樱桃消除延迟 136 | AConnect(ATime(10, 341 - 100), [] { 137 | ACard(ACHERRY_BOMB, 2, 9); 138 | }); 139 | 140 | AConnect(ATime(10 + 1, -298 + 1) /* 1 是为了兼容栈位带来的 1cs 偏差*/, [] { 141 | aIceFiller.Coffee(); 142 | }); 143 | 144 | // wave 20 145 | AConnect(ATime(20, 394 + 1 - 298) /* 1 是为了兼容栈位带来的 1cs 偏差*/, [] { 146 | aIceFiller.Coffee(); // 冰杀小偷 147 | }); 148 | 149 | AConnect(ATime(20, 1000), [] { 150 | aCobManager.RecoverFire({{2, 9}, {5, 9}, {2, 8.5}, {5, 8.5}}); 151 | }); 152 | } -------------------------------------------------------------------------------- /tutorial/scripts/liang_yi/game1_13.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/scripts/liang_yi/game1_13.dat -------------------------------------------------------------------------------- /tutorial/scripts/liang_yi/liang_yi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * @Coding: utf-8 3 | * @Author: vector-wlc 4 | * @Date: 2023-01-02 10:36:03 5 | * @Description: 此脚本并不能保证任何成功率,并且目前不能挂机,主要演示如何使用 AvZ 编写无炮键控脚本 6 | */ 7 | #include "processor.h" 8 | 9 | #if __AVZ_VERSION__ < 230501 10 | #error "此脚本需要 AvZ2 2.3.3 230501 版本才能运行" 11 | #endif 12 | 13 | Processor processor; 14 | ATickRunner tickRunner; 15 | 16 | void AScript() 17 | { 18 | ASetZombies({ATT_4, AHY_32, ABY_23, AGL_7, ABC_12, AXC_15, AQQ_16, AQS_11, AKG_17, APJ_0, AHT_14, ABJ_20, ATL_22, AWW_8}); 19 | ASelectCards({AHY_16, ANGT_30, AHBG_14, AM_HBG_14, AHMG_15, AYTZD_2, AHBLJ_20, AWG_17, AXPG_8, ASYC_27}, 1); 20 | ASetReloadMode(AReloadMode::MAIN_UI); 21 | ACard(AWG_17, 6, 9); 22 | tickRunner.Start([] { 23 | processor.Observe(); 24 | processor.GenDangerGridVec(); 25 | if (!processor.IsPaused()) { 26 | processor.UseSquash(); 27 | processor.UsePuff(); 28 | processor.UseBlover(); 29 | } 30 | processor.ShovelPumpkin(); 31 | processor.UsePumpkin(); 32 | processor.Show(); 33 | }); 34 | 35 | for (int wave = 1; wave < 21; ++wave) { 36 | AConnect(ATime(wave, 2000), [] { 37 | // 使用灰烬之前暂停工作 38 | processor.Pause(); 39 | }); 40 | AConnect(ATime(wave, 900 - 320 - 100), [] { 41 | processor.UseIce(); 42 | }); 43 | } 44 | 45 | for (auto wave : {1, 3, 5, 7, 9, 10, 12, 14, 16, 18}) { 46 | AConnect(ATime(wave, 900), [] { 47 | processor.GenJalapenoAndCherryRow(); 48 | }); 49 | AConnect(ATime(wave, 2500 - 200 - 100), [] { 50 | processor.UseCherry(); 51 | }); 52 | } 53 | 54 | for (auto wave : {2, 4, 6, 8, 11, 13, 15, 17, 19, 20}) { 55 | AConnect(ATime(wave, 266), [] { 56 | processor.UseJalapeno(); 57 | }); 58 | AConnect(ATime(wave, 2500 - 200 - 100), [] { 59 | processor.UseDoom(); 60 | }); 61 | } 62 | 63 | AConnect(ATime(9, 2500), [] { 64 | processor.UseJalapeno(); 65 | processor.UseIce(); 66 | }); 67 | AConnect(ATime(10, 395 - 100), [] { // 核杀小偷 68 | processor.UseDoom(); 69 | }); 70 | AConnect(ATime(12, 1000), [] { // 第十二波核的使用位置和辣椒有关 71 | processor.GenDoomRow(); 72 | }); 73 | AConnect(ATime(19, 2500), [] { 74 | processor.UseIce(); 75 | }); 76 | AConnect(ATime(20, 0), [] { 77 | processor.GenJalapenoAndCherryRow(); 78 | }); 79 | AConnect(ATime(20, 395 - 100), [] { // 樱杀小偷 80 | ACard(AHY_16, 3, 4); 81 | ACard(ACHERRY_BOMB, 3, 4); 82 | }); 83 | AConnect(ATime(20, 2500), [] { 84 | processor.UseIce(); 85 | }); 86 | } -------------------------------------------------------------------------------- /tutorial/scripts/tian_tai_10/game1_13.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-wlc/AsmVsZombies/0b5b8e9078ababd601723b46dba6e14fbebec00b/tutorial/scripts/tian_tai_10/game1_13.dat -------------------------------------------------------------------------------- /tutorial/scripts/tian_tai_10/tian_tai_10.cpp: -------------------------------------------------------------------------------- 1 | // 这里使用天台十炮作为第三个键控脚本,天台十炮可以让我们体会到多炮列表的作用 2 | 3 | // **请强烈注意,我不是键控技术党,我是键控工具党,所以示例脚本的目的是教会大家如何使用本框架的接口,** 4 | // **脚本中的一些数值并不是科学的数值,科学的数值请询问当前植吧顶尖技术党或者查看相关的帖子和B站动态等,但由于本人早已不是键控技术党,** 5 | // **而且没有精力和兴趣去学相关的键控技术知识。** 6 | 7 | // **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 8 | 9 | // **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 10 | 11 | // **所以不要把示例脚本视作完美无缺的,而只是把他视作学习本框架接口的工具** 12 | 13 | #include 14 | 15 | ACobManager windCob; 16 | ACobManager groudCob; 17 | 18 | void AScript() 19 | { 20 | ASetZombies({ 21 | AHY_32, 22 | ABY_23, 23 | AGL_7, 24 | AFT_21, 25 | AQQ_16, 26 | ABC_12, 27 | ATT_18, 28 | ATT_4, 29 | ALZ_2, 30 | APJ_0, 31 | ABJ_20, 32 | }); 33 | ASelectCards({ 34 | AICE_SHROOM, // 寒冰菇 35 | AM_ICE_SHROOM, // 模仿寒冰菇 36 | ACOFFEE_BEAN, // 咖啡豆 37 | ADOOM_SHROOM, // 毁灭菇 38 | AFLOWER_POT, // 花盆 39 | ASQUASH, // 倭瓜 40 | ACHERRY_BOMB, // 樱桃炸弹 41 | ABLOVER, // 三叶草 42 | APUMPKIN, // 南瓜头 43 | APUFF_SHROOM, // 小喷菇 44 | }); 45 | 46 | AConnect(ATime(1, -599), [] { 47 | windCob.SetList({{1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}}); 48 | groudCob.SetList({{1, 6}, {2, 6}, {3, 6}, {4, 6}, {5, 6}}); 49 | }); 50 | for (int wave = 1; wave < 21; ++wave) { 51 | AConnect(ATime(wave, 3475 / 5 - 200 - 387), [] { 52 | windCob.RoofFire(2, 8.8625); 53 | groudCob.RoofFire(4, 8.8625); 54 | }); 55 | } 56 | 57 | AConnect(ATime(10, 3475 / 5 - 200 - 100), [] { 58 | ACard({{AFLOWER_POT, 2, 9}, {ACHERRY_BOMB, 2, 9}}); 59 | }); 60 | 61 | for (auto wave : {9, 19, 20}) { 62 | AConnect(ATime(wave, 3475 / 5 - 200 - 387), [] { 63 | windCob.RecoverFire(2, 9); 64 | groudCob.RecoverFire(5, 9); 65 | windCob.RecoverFire(2, 9); 66 | groudCob.RecoverFire(5, 9); 67 | }); 68 | } 69 | 70 | AConnect(ATime(20, -298 - 1), [] { 71 | ACard({{AFLOWER_POT, 2, 9}, {AICE_SHROOM, 2, 9}, {ACOFFEE_BEAN, 2, 9}}); 72 | }); 73 | } --------------------------------------------------------------------------------