diff --git a/pvz/__init__.py b/pvz/__init__.py index 4935f79..162b3a1 100644 --- a/pvz/__init__.py +++ b/pvz/__init__.py @@ -53,6 +53,7 @@ ## 功能修改 from .extra import set_zombies as SetZombies +from .extra import get_zombies as GetZombies ## 选卡/更新炮列表 from .extra import select_seeds_and_lets_rock as SelectCards @@ -99,6 +100,7 @@ "PressKeys", # 功能修改 "SetZombies", + "GetZombies", # 选卡/更新炮列表 "SelectCards", "UpdatePaoList", @@ -143,6 +145,10 @@ def _on_start(): # set_dpi_scale(1.25) # 出错则手动设置 if find_pvz(): + if pvz_ver() != "1.2.0.1096": + set_addr(0x6A9EC0, 0x768, 0x00) + else: + set_addr(0x731C50, 0x868, 0x18) ui = game_ui() if ui in (2, 3): set_pvz_foreground() @@ -153,7 +159,7 @@ def _on_start(): else: critical("游戏未开启或者游戏版本不受支持!") - enable_logging(False) # 是否输出调试日志 + enable_logging(True) # 是否输出调试日志 set_logging_level("INFO") diff --git a/pvz/core.py b/pvz/core.py index ecaf447..3ac7cf4 100644 --- a/pvz/core.py +++ b/pvz/core.py @@ -477,6 +477,12 @@ def find_pvz(): pvz_version = "1.2.0.1065" info("已找到游戏 1.2.0.1065 !!!") return True + elif True: + # TODO 缺少 steam 版检测机制 + # 暂时默认非 1051、1065 的版本一定是 steam 版 + pvz_version = "1.2.0.1096" + info("已找到游戏 1.2.0.1096 !!!") + return True else: pvz_version = None warning("不支持的游戏版本 !!!") @@ -567,6 +573,7 @@ def read_memory(data_type, *address, array=1): size = ctypes.sizeof(buffer) success = ReadProcessMemory(pvz_handle, offset, ctypes.byref(buffer), size, ctypes.byref(bytes_read)) if success == 0 or bytes_read.value != size: + memory_lock.release() critical("读取内存失败, 错误代码 %d." % GetLastError()) else: @@ -575,6 +582,7 @@ def read_memory(data_type, *address, array=1): buff = ctypes.create_string_buffer(size) # 目标数据缓冲 success = ReadProcessMemory(pvz_handle, offset, ctypes.byref(buff), size, ctypes.byref(bytes_read)) if success == 0 or bytes_read.value != size: + memory_lock.release() critical("读取内存失败, 错误代码 %d." % GetLastError()) result = struct.unpack(fmt_str, buff.raw) @@ -627,6 +635,7 @@ def write_memory(data_type, values, *address): size = ctypes.sizeof(buffer) success = ReadProcessMemory(pvz_handle, offset, ctypes.byref(buffer), size, ctypes.byref(bytes_read)) if success == 0 or bytes_read.value != size: + memory_lock.release() critical("读取内存失败, 错误代码 %d." % GetLastError()) else: @@ -637,6 +646,7 @@ def write_memory(data_type, values, *address): buff.value = struct.pack(fmt_str, *values) # 将目标数据载入缓冲区 success = WriteProcessMemory(pvz_handle, offset, ctypes.byref(buff), size, ctypes.byref(bytes_written)) if success == 0 or bytes_written.value != size: + memory_lock.release() critical("写入内存失败, 错误代码 %d." % GetLastError()) memory_lock.release() @@ -733,6 +743,12 @@ def asm_push(code): asm_add_dword(code) +# push 0x12 +def asm_push_byte(code): + asm_add_byte(0x6A) + asm_add_byte(code) + + # mov exx, 0x12345678 asm_mov_exx_code = { "eax": [0xB8], @@ -902,15 +918,19 @@ def asm_code_inject(): def asm_code_inject_safely(): if pvz_ver() == "1.0.0.1051": write_memory("unsigned char", 0xFE, 0x00552014) - else: + elif pvz_ver() == "1.2.0.1065": write_memory("unsigned char", 0xFE, 0x00552244) + else: + write_memory("unsigned char", 0xFE, 0x005DD25E) time.sleep(0.01) if is_valid(): asm_code_inject() if pvz_ver() == "1.0.0.1051": write_memory("unsigned char", 0xDB, 0x00552014) - else: + elif pvz_ver() == "1.2.0.1065": write_memory("unsigned char", 0xDB, 0x00552244) + else: + write_memory("unsigned char", 0xC8, 0x005DD25E) ### 键盘操作 diff --git a/pvz/extra.py b/pvz/extra.py index 0e75596..e0c3b50 100644 --- a/pvz/extra.py +++ b/pvz/extra.py @@ -2,6 +2,19 @@ from .core import * +# 内存基址、二级偏移、三级偏移 +base_addr = 0x6A9EC0 +off2 = 0x768 +off3 = 0x00 + + +def set_addr(_base, _off2, _off3): + global base_addr, off2, off3 + base_addr = _base + off2 = _off2 + off3 = _off3 + + ### 读取常用信息 @@ -9,10 +22,7 @@ def game_on(): """ @返回值 (bool): 游戏是否开启, 没开则会尝试重新查找一次. """ - if is_valid(): - return True - else: - return find_pvz() + return is_valid() or find_pvz() def game_ui(): @@ -21,14 +31,20 @@ def game_ui(): 1: 主界面, 2: 选卡, 3: 正常游戏/战斗, 4: 僵尸进屋, 7: 模式选择. """ - return read_memory("int", 0x6A9EC0, 0x7FC) + if base_addr == 0x6A9EC0: + return read_memory("int", base_addr, 0x7FC) + else: + return read_memory("int", base_addr, 0x91C) def game_mode(): """ @返回值 (int): 游戏模式, 13 为生存无尽. """ - return read_memory("int", 0x6A9EC0, 0x7F8) + if base_addr == 0x6A9EC0: + return read_memory("int", base_addr, 0x7F8) + else: + return read_memory("int", base_addr, 0x918) def game_scene(): @@ -37,63 +53,63 @@ def game_scene(): 0: 白天, 1: 黑夜, 2: 泳池, 3: 浓雾, 4: 屋顶, 5: 月夜, 6: 蘑菇园, 7: 禅境花园, 8: 水族馆, 9: 智慧树. """ - return read_memory("int", 0x6A9EC0, 0x768, 0x554C) + return read_memory(base_addr, off2, off3 + 0x554C) def game_paused(): """ @返回值 (bool): 当前游戏是否暂停. """ - return read_memory("bool", 0x6A9EC0, 0x768, 0x164) + return read_memory("bool", base_addr, off2, off3 + 0x164) def mouse_in_game(): """ @返回值 (bool): 鼠标是否在游戏窗口内部. """ - return read_memory("bool", 0x6A9EC0, 0x768, 0x138, 0x18) # 0x6A9EC0, 0x768, 0x59 + return read_memory("bool", base_addr, off2, off3 + 0x138, 0x18) def mouse_have_something(): """ @返回值 (bool): 鼠标是否选中卡炮或铲子. """ - return read_memory("int", 0x6A9EC0, 0x768, 0x138, 0x30) in (1, 6, 8) + return read_memory("int", base_addr, off2, off3 + 0x138, 0x30) in (1, 6, 8) def game_clock(): """ @返回值 (int): 内部时钟, 游戏暂停和选卡时会暂停计时. """ - return read_memory("int", 0x6A9EC0, 0x768, 0x5568) + return read_memory("int", base_addr, off2, off3 + 0x5568) def wave_init_countdown(): """ @返回值 (int): 刷新倒计时初始值. """ - return read_memory("int", 0x6A9EC0, 0x768, 0x55A0) + return read_memory("int", base_addr, off2, off3 + 0x55A0) def wave_countdown(): """ @返回值 (int): 下一波刷新倒计时, 触发刷新时重置为 200, 减少至 1 后刷出下一波. """ - return read_memory("int", 0x6A9EC0, 0x768, 0x559C) + return read_memory("int", base_addr, off2, off3 + 0x559C) def huge_wave_countdown(): """ @返回值 (int): 大波刷新倒计时, 对于旗帜波, 刷新倒计时减少至 4 后停滞, 由该值代替减少. """ - return read_memory("int", 0x6A9EC0, 0x768, 0x55A4) + return read_memory("int", base_addr, off2, off3 + 0x55A4) def current_wave(): """ @返回值 (int): 已刷新波数. """ - return read_memory("int", 0x6A9EC0, 0x768, 0x557C) + return read_memory("int", base_addr, off2, off3 + 0x557C) ### 修改出怪 @@ -101,6 +117,9 @@ def current_wave(): # 从出怪种子生成出怪类型 def update_zombies_type(): + if base_addr != 0x6A9EC0: + info("Steam 版暂不支持生成出怪类型") + return write_memory("bool", [False] * 33, 0x6A9EC0, 0x768, 0x54D4) asm_init() asm_mov_exx_dword_ptr("esi", 0x6A9EC0) @@ -113,6 +132,9 @@ def update_zombies_type(): # 从出怪类型生成出怪列表 def update_zombies_list(): + if base_addr != 0x6A9EC0: + info("Steam 版暂不支持生成出怪列表") + return asm_init() asm_mov_exx_dword_ptr("edi", 0x6A9EC0) asm_mov_exx_dword_ptr_exx_add("edi", 0x768) @@ -123,6 +145,9 @@ def update_zombies_list(): # 更新选卡界面出怪预览 def update_zombies_preview(): + if base_addr != 0x6A9EC0: + info("Steam 版暂不支持更新出怪预览") + return write_memory("byte", 0x80, 0x0043A153) if pvz_ver() == "1.0.0.1051" else write_memory("byte", 0x80, 0x0043A1C3) asm_init() asm_mov_exx_dword_ptr("ebx", 0x6A9EC0) @@ -162,6 +187,10 @@ def set_internal_spawn(zombies=None): if not (game_on() and game_ui() in (2, 3)): return + if base_addr != 0x6A9EC0: + info("Steam 版暂不支持修改出怪") + return + if zombies is None: zombies = [] zombies += [0] # 普僵必出 @@ -186,6 +215,10 @@ def set_customize_spawn(zombies=None): if not (game_on() and game_ui() in (2, 3)): return + if base_addr != 0x6A9EC0: + info("Steam 版暂不支持修改出怪") + return + if zombies is None: zombies = [0] # 默认普僵 zombies = [zombie_name_to_index(z) for z in zombies] @@ -273,6 +306,28 @@ def set_zombies(zombies=None, mode="极限刷怪"): error("未知刷怪模式: %s." % mode) +def get_zombies(zombie="红眼"): + """ + 检查指定的僵尸在本次选卡的每波里各出现多少只. + + @参数 zombie(str/int): 指定要查询的僵尸名字或代号. + + @返回值 (list[int]): 长度为 21 的数组. 下标 0 占位, + + 下标 1~20 依次对应指定的僵尸在对应波次里的出现数量. + """ + index = zombie_name_to_index(zombie) # 转换成数字 + z = read_memory("int", base_addr, off2, off3 + 0x6B4, array=1000) + counts = [0] * 21 # 20 波各出了多少只 + for i in range(20): # 共 20 波 + for j in range(50): # 每波 50 只 + if z[50 * i + j] == -1: + break + if z[50 * i + j] == index: + counts[i + 1] += 1 + return counts + + ### 选卡 # (50, 160) 为左上角卡片中心坐标 @@ -287,8 +342,8 @@ def set_zombies(zombies=None, mode="极限刷怪"): IMITATER_SEED_0_0_Y = 160 SEED_WIDTH = 50 SEED_HEIGHT = 70 -IMITATER_X = 490 -IMITATER_Y = 550 +IMITATER_X = 470 +IMITATER_Y = 580 IMITATER_SHOW_UP = 0 SEED_DELAY_TIME = 5 @@ -325,7 +380,10 @@ def select_seed_by_crood(row, col, imitater=False): y = SEED_0_0_Y + (row - 1) * (SEED_HEIGHT + 0) # 不小心点进了图鉴或者商店 - window_type = read_memory("int", 0x6A9EC0, 0x320, 0x88, 0xC) + if base_addr == 0x6A9EC0: + window_type = read_memory("int", base_addr, 0x320, 0x88, 0xC) + else: + window_type = read_memory("int", base_addr, 0x320, 0xA0, 0x1C) if window_type == 1: left_click(720, 580) thread_sleep_for(5) @@ -404,8 +462,8 @@ def _(seed): # 检查已选卡片是否完全相符 def slots_exact_match(seeds_selected): - slots_count = read_memory("int", 0x6A9EC0, 0x768, 0x144, 0x24) - slots_selected = read_memory("int", 0x6A9EC0, 0x774, 0xD24) + slots_count = read_memory("int", base_addr, off2, off3 + 0x144, 0x24) + slots_selected = read_memory("int", base_addr, off2 + 0x0C, off3 + 0xD24) if slots_selected < slots_count: return False match = True @@ -414,8 +472,8 @@ def slots_exact_match(seeds_selected): seed_index = (row - 1) * 8 + (col - 1) if imitater: seed_index = 48 - seed_plant = read_memory("int", 0x6A9EC0, 0x774, 0xC4 + seed_index * 0x3C) - seed_status = read_memory("int", 0x6A9EC0, 0x774, 0xC8 + seed_index * 0x3C) + seed_plant = read_memory("int", base_addr, off2 + 0x0C, off3 + 0xC4 + seed_index * 0x3C) + seed_status = read_memory("int", base_addr, off2 + 0x0C, off3 + 0xC8 + seed_index * 0x3C) # !卡片对应的植物正确并且位于卡槽中 if not (seed_plant == seed_index and seed_status == 1): match = False @@ -425,7 +483,7 @@ def slots_exact_match(seeds_selected): # 清空卡槽所有卡片 def clear_slots(): - slots_count = read_memory("int", 0x6A9EC0, 0x768, 0x144, 0x24) + slots_count = read_memory("int", base_addr, off2, off3 + 0x144, 0x24) for slot_index in reversed(range(slots_count)): # 逆序 slot_index += 1 if slots_count == 10: @@ -451,7 +509,10 @@ def select_all_seeds(seeds_selected=None): """ # 不小心点进了图鉴或者商店 - window_type = read_memory("int", 0x6A9EC0, 0x320, 0x88, 0xC) + if base_addr == 0x6A9EC0: + window_type = read_memory("int", base_addr, 0x320, 0x88, 0xC) + else: + window_type = read_memory("int", base_addr, 0x320, 0xA0, 0x1C) if window_type == 1: left_click(720, 580) thread_sleep_for(5) @@ -459,8 +520,8 @@ def select_all_seeds(seeds_selected=None): left_click(430, 550) thread_sleep_for(5) - default_seeds = [40, 41, 42, 43, 44, 45, 46, 47, 8, 8 + 48] - slots_count = read_memory("int", 0x6A9EC0, 0x768, 0x144, 0x24) + default_seeds = [8 + 48, 8, 40, 41, 42, 43, 44, 45, 46, 47] + slots_count = read_memory("int", base_addr, off2, off3 + 0x144, 0x24) # 默认八张紫卡和两张免费卡 if seeds_selected is None: @@ -501,12 +562,12 @@ def select_all_seeds(seeds_selected=None): def lets_rock(): - while read_memory("bool", 0x6A9EC0, 0x768, 0x15C, 0x2C): # 位于选卡界面 + while read_memory("bool", base_addr, off2, off3 + 0x15C, 0x2C): # 位于选卡界面 left_down(234, 567) thread_sleep_for(1) left_up(234, 567) thread_sleep_for(10) - while read_memory("int", 0x6A9EC0, 0x320, 0x94) != 0: # 出现了对话框 + while read_memory("int", base_addr, 0x320, off3 + 0x94) != 0: # 出现了对话框 left_click(320, 400) thread_sleep_for(10) @@ -584,8 +645,8 @@ def update_seeds_list(): seeds_in_slot = [None] * (48 * 2) slot_seeds = [None] * 10 - slots_count = read_memory("int", 0x6A9EC0, 0x768, 0x144, 0x24) - slots_offset = read_memory("unsigned int", 0x6A9EC0, 0x768, 0x144) + slots_count = read_memory("int", base_addr, off2, off3 + 0x144, 0x24) + slots_offset = read_memory("unsigned int", base_addr, off2, off3 + 0x144) for i in range(slots_count): seed_type = read_memory("int", slots_offset + 0x5C + i * 0x50) seed_imitater_type = read_memory("int", slots_offset + 0x60 + i * 0x50) @@ -670,8 +731,8 @@ def get_seed_by_index(index): # 更新卡槽格数和场景地图 def update_game_scene(): global slots_count, game_scene - slots_count = read_memory("int", 0x6A9EC0, 0x768, 0x144, 0x24) - game_scene = read_memory("int", 0x6A9EC0, 0x768, 0x554C) + slots_count = read_memory("int", base_addr, off2, off3 + 0x144, 0x24) + game_scene = read_memory("int", base_addr, off2, off3 + 0x554C) info("更新卡槽格数 %d." % slots_count) info("更新场景地图 %s." % scenes[game_scene]) @@ -709,7 +770,7 @@ def safe_click(): 即右键单击左上角, 用于取消之前的 (可能未完成的) 操作以避免冲突. """ - right_click(0, 0) + right_click(40, 40) def click_seed(seed): @@ -855,8 +916,8 @@ def update_cob_cannon_list(cobs=None): global cob_list, cob_index cob_list_tmp = [] - plants_count_max = read_memory("unsigned int", 0x6A9EC0, 0x768, 0xB0) - plants_offset = read_memory("unsigned int", 0x6A9EC0, 0x768, 0xAC) + plants_count_max = read_memory("unsigned int", base_addr, off2, off3 + 0xB0) + plants_offset = read_memory("unsigned int", base_addr, off2, off3 + 0xAC) for i in range(plants_count_max): plant_dead = read_memory("bool", plants_offset + 0x141 + 0x14C * i) plant_crushed = read_memory("bool", plants_offset + 0x142 + 0x14C * i) @@ -1008,7 +1069,7 @@ def fire_cob(*croods): @fire_cob.register(int) -def _(fall_row, fall_col): +def _(fall_row, fall_col, delay_cs = 0): global cob_list, cob_index cob_count = len(cob_list) if cob_count == 0: @@ -1016,7 +1077,7 @@ def _(fall_row, fall_col): cob_lock.acquire() cob_row, cob_col = cob_list[cob_index] - fire_cob_by_crood(cob_row, cob_col, fall_row, fall_col) + fire_cob_by_crood(cob_row, cob_col, fall_row, fall_col, delay_cs) cob_index += 1 cob_index %= cob_count cob_lock.release() @@ -1148,22 +1209,25 @@ def skip_cob_index(num): ### 直接发炮 # 炮身点击次数 -CLICK_COUNT = 3 +CLICK_COUNT = 9 # 无视内置炮列表直接指定炮位和落点 -def fire_cob_by_crood(cob_row, cob_col, fall_row, fall_col): +def fire_cob_by_crood(cob_row, cob_col, fall_row, fall_col, delay_cs = 0): mouse_lock.acquire() safe_click() for _ in range(CLICK_COUNT): # 点炮位置稍微偏离, 配合改内存解决炮粘手的问题 click_grid(cob_row + 2 / 85, cob_col - 2 / 80) + if delay_cs > 0: + info("(%d, %d) 炮已捏在手上, 将于 %dcs 后发射至 (%d, %d)." % (cob_row, cob_col, delay_cs, fall_row, fall_col)) + game_delay_for(delay_cs) click_grid(fall_row, fall_col) safe_click() mouse_lock.release() - info("从 (%d, %d) 向 (%d, %d) 发射玉米炮." % (cob_row, cob_col, fall_row, fall_col)) + info("从 (%d, %d) 向 (%d, %.1f) 发射玉米炮." % (cob_row, cob_col, fall_row, fall_col)) ### 阻塞延时 @@ -1183,7 +1247,7 @@ def game_delay_for(time_cs): if time_cs > 0: clock = game_clock() - while (game_clock() - clock) < time_cs: + while game_ui() == 3 and (game_clock() - clock) < time_cs: delay_a_little_time() elif time_cs == 0: pass @@ -1269,15 +1333,13 @@ def until_relative_time_after_refresh(time_relative_cs, wave): until_countdown(refresh_trigger[wave - 1], is_huge_wave) # 计算实际倒计时数值 - _wave_countdown = wave_countdown() _huge_wave_countdown = huge_wave_countdown() - if is_huge_wave: - if _wave_countdown in (4, 5): - countdown = _huge_wave_countdown - else: - countdown = _wave_countdown - 5 + 750 + if not is_huge_wave: + countdown = wave_countdown() + elif _huge_wave_countdown > 0: + countdown = _huge_wave_countdown else: - countdown = _wave_countdown + countdown = wave_countdown() - 5 + 750 # 计算刷新时间点(倒计时变为下一波初始值时)的时钟数值 _game_clock = game_clock() @@ -1417,10 +1479,9 @@ def auto_collect(collect_items=None, interval_cs=12): info("启动自动收集线程.") while game_ui() == 3: - items_count = read_memory("int", 0x6A9EC0, 0x768, 0xF4) - items_count_max = read_memory("int", 0x6A9EC0, 0x768, 0xE8) - items_offset = read_memory("int", 0x6A9EC0, 0x768, 0xE4) - + items_count = read_memory("int", base_addr, off2, off3 + 0xF4) + items_count_max = read_memory("int", base_addr, off2, off3 + 0xE8) + items_offset = read_memory("int", base_addr, off2, off3 + 0xE4) if items_count == 0: thread_sleep_for(interval_cs) continue @@ -1483,8 +1544,8 @@ def get_seeds_index(seed): seed %= 48 seed_indexes = [] - slots_count = read_memory("int", 0x6A9EC0, 0x768, 0x144, 0x24) - slots_offset = read_memory("unsigned int", 0x6A9EC0, 0x768, 0x144) + slots_count = read_memory("int", base_addr, off2, off3 + 0x144, 0x24) + slots_offset = read_memory("unsigned int", base_addr, off2, off3 + 0x144) for i in range(slots_count): seed_type = read_memory("int", slots_offset + 0x5C + i * 0x50) seed_imitater_type = read_memory("int", slots_offset + 0x60 + i * 0x50) @@ -1498,8 +1559,8 @@ def get_plants_croods(): 获取场上植物坐标. """ croods = [] - plants_count_max = read_memory("unsigned int", 0x6A9EC0, 0x768, 0xB0) - plants_offset = read_memory("unsigned int", 0x6A9EC0, 0x768, 0xAC) + plants_count_max = read_memory("unsigned int", base_addr, off2, off3 + 0xB0) + plants_offset = read_memory("unsigned int", base_addr, off2, off3 + 0xAC) for i in range(plants_count_max): plant_dead = read_memory("bool", plants_offset + 0x141 + 0x14C * i) plant_crushed = read_memory("bool", plants_offset + 0x142 + 0x14C * i) @@ -1520,7 +1581,7 @@ def get_block_type(*crood): else: row, col = crood row, col = row - 1, col - 1 - return read_memory("int", 0x6a9ec0, 0x768, 0x168 + row * 0x04 + col * 0x18) + return read_memory("int", base_addr, off2, off3 + 0x168 + row * 0x04 + col * 0x18) # 存冰位 @@ -1563,9 +1624,7 @@ def auto_fill_ice(spots=None, total=0x7FFFFFFF): global ice_spots, ice_total ice_spots = spots ice_total = total - - slots_offset = read_memory("unsigned int", 0x6A9EC0, 0x768, 0x144) - + slots_offset = read_memory("unsigned int", base_addr, off2, off3 + 0x144) ice_seeds_index = get_seeds_index("寒冰菇") # 获取所有寒冰菇卡片的下标 if ice_seeds_index == []: error("卡槽没有寒冰菇, 退出自动存冰.") @@ -1612,18 +1671,14 @@ def auto_fill_ice(spots=None, total=0x7FFFFFFF): break # 如果该位置无植物则尝试存冰 - if pvz_ver() == "1.0.0.1051": - seed_ice_cost = read_memory("int", 0x69F2C0 + 14 * 0x24) - else: - seed_ice_cost = read_memory("int", 0x69F2D0 + 14 * 0x24) - sun = read_memory("int", 0x6A9EC0, 0x768, 0x5560) - if sun < seed_ice_cost: + sun = read_memory("int", base_addr, off2, off3 + 0x5560) + if sun < 75: thread_sleep_for(1) continue block_type = get_block_type(spot) # 1.草地 2.裸地 3.泳池 16.睡莲 33.花盆 if (spot not in ice_spot_which_has_plant \ - and sun >= seed_ice_cost \ + and sun >= 75 \ and ((block_type == 1 and game_scene not in (4, 5)) \ or (block_type == 1 and game_scene in (4, 5) and (33, spot[0], spot[1]) in plants) \ or (block_type == 3 and (16, spot[0], spot[1]) in plants))): @@ -1652,11 +1707,17 @@ def auto_fill_ice(spots=None, total=0x7FFFFFFF): info("停止自动存冰线程.") -def activate_ice(): +def activate_ice(spots=None): """ 点冰. 使用咖啡豆激活存冰, 优先点临时位. 该函数需要配合自动存冰线程 IceSpots() 使用. + + @参数 spots(list): 咖啡豆的放置位置. 函数会选中咖啡豆, 并按指定的坐标顺序依次尝试放置. + + 如空置, 则默认为调用 IceSpots() 时指定的存冰点的逆序. + + 指定该参数可激活未在 IceSpots() 指定的位置里存的冰. """ coffee_index = get_index_by_seed(35) # 咖啡豆位置 if coffee_index is None: @@ -1665,9 +1726,10 @@ def activate_ice(): mouse_lock.acquire() safe_click() click_seed(coffee_index) - for spot in reversed(ice_spots): # 优先点临时位 + for spot in spots or reversed(ice_spots): # 优先点临时位 row, col = spot row -= 0.3 # 咖啡豆 理想种植坐标偏上约 30px click_grid((row, col)) safe_click() mouse_lock.release() + info("唤醒寒冰菇.")