最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

植物大戰(zhàn)僵尸 Steam 版鍵控指南

2023-01-18 17:23 作者:ココアお姉ちゃん  | 我要投稿

貼吧里的鍵控資料大部分都是針對第一版(即 1.0.0.1051?版)的,偶爾也有吧友問過 Steam?版該如何鍵控,但得到的答案都是【沒法鍵控】。

但,實際上不是沒法鍵控,而是大家都沒去做而已。辦法還是有的。

1.?為什么非 Steam?版不可

先說我為啥要做 Steam?版的鍵控框架。首先,我的 PvZ?是從 Steam?上買的,就是很樸素地想為童年補個票而已。當(dāng)然,童年時我玩的是只有冒險模式 +?小游戲的 PvZ,生存無盡因為沒有掌握布陣知識,胡搞瞎搞,最多撐到 20?flags 就沒了。我入了 Steam?版后才真正開始玩無盡,現(xiàn)在五大場地的無盡都已經(jīng)過了 400 flags?了。我的心血全在 Steam?版上,自然沒有動力切換到原版重新開始。

另外 Steam?版修復(fù)了原版的上界之風(fēng) bug,同時還平衡了舞王,讓伴舞不至于一出土就開始啃 5、6?列炮,相對于原版來說,對萌新更友好一點。這也是我留在 Steam?版的原因之一(雖然不是主要原因)。

2.?準(zhǔn)備工作

這里我們要用到 @囧丫乙?大佬做好的框架:pvzscripts。這是一個用 Python?寫的,作用于 1.0.0.1051?和 1.2.0.1065?版本的鍵控框架。我們在此基礎(chǔ)上稍做修改,便可得到一個適配 Steam?版本的框架。地址:https://github.com/lmintlcx/pvzscripts

我們將其克隆下來。打開命令提示符,輸入如下命令:

以上代碼會將整個倉庫克隆到你的電腦上的 E:\pvzscripts?目錄里。當(dāng)然這個目錄名你可以自己任意指定,想克隆到別的地方也是可以的。以下為了方便說明,我全部假設(shè)你的倉庫目錄為 E:\pvzscripts。

然后,我們要對原始代碼做一些修改。用 Visual Studio Code?打開 E:\pvzscripts?目錄,然后打開 E:\pvzscripts\pvz\core.py?文件,找到 find_pvz?函數(shù),做如下修改:

特別注意:這里之所以寫?elif True,是因為我暫時沒有找到檢測 Steam?版本的方法,所以這里假設(shè)既不是 1.0.0.1051?又不是 1.2.0.1065?時就一定是 Steam?版。這只是讓框架能跑起來的一個臨時辦法,是不嚴(yán)謹(jǐn)?shù)?。一旦后續(xù)找到了判斷手段,我也會相應(yīng)地更新判斷條件。

接下來,找到 read_memory?函數(shù),做如下修改:

如圖所示,read_memory 函數(shù)一共有兩處地方需要做這樣的修改:

兩條?critial?語句的作用是“讓程序拋出異常并輸出日志”,但在此之前我們必須要將內(nèi)存鎖給釋放掉,否則程序無法正常結(jié)束。對于接下來的 write_memory?函數(shù),也是同理:

找到 asm_code_inject_safely?函數(shù),做出如下修改:

接下來對 E:\pvzscripts\pvz\extra.py?文件進行修改。其實本質(zhì)都是在讀內(nèi)存,只不過不同版本間的內(nèi)存地址不一樣而已。所以我們需要改動的只是各種內(nèi)存地址的數(shù)字而已。

Steam?版的大部分內(nèi)存地址我是通過查閱 @囧丫乙?做的修改器?PvZ Toolkit?里的源碼找到的(地址:https://github.com/lmintlcx/pvztoolkit)。還有少部分地址是通過各種百度、谷歌,以及自己瞎試找出來的。

由于不同版本的內(nèi)存基址不一樣,所以我們有必要用三個變量來存儲一下當(dāng)前運行中的版本的內(nèi)存基址、二級偏移、三級偏移。首先我們打開 extra.py 文件,在 from .core import * 后面加上這樣的代碼:

然后我們開始對系統(tǒng) API 進行修改。修改的內(nèi)容大同小異,都是將原先寫死的內(nèi)存基址、二級偏移、三級偏移常量改寫成變量。

game_ui、game_mode、game_scene?函數(shù):

game_paused、mouse_in_game、mouse_have_something、game_clock?函數(shù):

wave_init_countdown、wave_countdown、huge_wave_countdown、current_wave?函數(shù):

下面的和修改出怪相關(guān)的代碼,由于涉及到寫內(nèi)存,危險度較大,再加上我平時沖關(guān)的時候并不會去調(diào)整出怪,都是見招拆招,所以這部分我就沒有適配了,判斷到是 Steam?版時直接短路 return:

接下來是模仿者的點擊坐標(biāo)。實際使用時發(fā)現(xiàn)點擊 (490, 550) ,即模仿者的中心點時,特別容易點到商店、圖鑒甚至是僵尸身上,經(jīng)常連續(xù)四次選卡都失敗。改成 (470, 580)?即模仿者的左下角時,問題有了顯著改善。因此把 IMITATER_X?改成 470,把 IMITATER_Y?改成 580。

繼續(xù),select_seed_by_crood?函數(shù):

slots_exact_match、clear_slots 函數(shù):

select_all_seeds?函數(shù):

lets_rock?函數(shù):

update_seeds_list?函數(shù):

update_game_scene?函數(shù):

update_cob_cannon_list?函數(shù):

auto_collect?函數(shù):

get_seeds_index、get_plants_croods 函數(shù):

get_block_type?函數(shù):

auto_fill_ice?函數(shù):

下面這個讀取陽光和寒冰菇價格的部分我改動的地方比較多。原代碼里寒冰菇的價格是通過讀取一個特定內(nèi)存找到的,但是這片內(nèi)存在 Steam?版里對應(yīng)的地址是多少,我并沒有找到。后來想了想,決定不讀內(nèi)存,轉(zhuǎn)而把價格寫死為 75。于是就改成了下面這個樣子:

至此,我們還有最后一處地方要改動。我們需要在初始化框架的時候調(diào)用 extra 里的 set_addr 函數(shù),以便正確設(shè)置內(nèi)存基址、二級偏移、三級偏移。我們打開 __init__.py 文件,找到 on_start 函數(shù),然后添加以下代碼:

改動完成后,別忘了保存這些代碼文件。然后,我們就可以愉快地編寫 Steam?版的鍵控腳本啦!

3.?腳本中可以使用的函數(shù)

SelectCards,在選卡界面選擇本輪要使用的卡片,參數(shù)為字符串?dāng)?shù)組,指定卡片時不是必須使用原名,也可以使用各種別名。對于模仿者,可以在原卡名字前加上“模仿者”、“復(fù)制”、“白”等前綴。必須在選卡時調(diào)用,其余時刻調(diào)用無效。舉例:

UpdatePaoList,更新場上的炮列表。參數(shù)為玉米炮的后輪坐標(biāo)數(shù)組。通常情況下無需調(diào)用此函數(shù),導(dǎo)入 pvz?庫時程序會自動掃描一遍場上的玉米炮。該函數(shù)一般在收尾階段使用,使用指定的炮收尾,以防本次選卡結(jié)束后,下一次運行腳本時發(fā)炮順序錯亂。舉例:

Prejudge,參數(shù)為一個相對時間和一個波數(shù),令程序阻塞到指定波數(shù)的指定時間。舉例:

該函數(shù)每波都需要調(diào)用一次。首波有 6?秒的準(zhǔn)備時間,Prejudge?時間最早可以到 -599;旗幟波有 7.5?秒的準(zhǔn)備時間(紅字時間),Prejudge?時間最早可以到 -750;通常波則只有 2?秒的準(zhǔn)備時間,Prejudge?時間最早到 -200。

但是這里要注意,Python?的精度沒有 C/C++?那么高,如果 Prejudge?的時間點過于極限,則有可能導(dǎo)致實際調(diào)用 Prejudge?時,需要到達的時間已經(jīng)過去,導(dǎo)致程序拋出異常。所以這里建議使用 -195?等時間,留出一些誤差余量。

Delay,參數(shù)為一個時間長度。令程序阻塞這么長時間。游戲暫停時,該計時也會跟著暫停。舉例:

Until,參數(shù)為一個時間點,令程序阻塞到當(dāng)前波的指定時間。實際效果跟 Prejudge?一樣,區(qū)別在于 Prejudge?只能每波調(diào)用一次,而 Until?在同一波內(nèi)不限制調(diào)用次數(shù)。舉例:

Card,參數(shù)為卡片名稱,及放置卡片的 (y, x)?坐標(biāo)。在 (y, x)?處用指定的卡。舉例:

Shovel,參數(shù)為一個或多個?(y, x)?坐標(biāo)。對指定的坐標(biāo)使用鏟子。舉例:

注意,當(dāng)對應(yīng)的格子有南瓜,且想要鏟除的是南瓜時,需要將對應(yīng)的 y?坐標(biāo)向下偏移 0.1,以免鏟到南瓜里的本體。

Pao,參數(shù)為一個或多個 (y, x)?坐標(biāo)。向指定的位置發(fā)炮。由于 Steam?版修復(fù)了上界之風(fēng) bug,所以屋頂發(fā)炮仍然可以直接調(diào)用 Pao?函數(shù),不需要像原版那樣調(diào)用 RoofPao?函數(shù)。舉例:

Skip,跳炮?;居糜谑瘴膊ǎ^一定數(shù)量的炮,以便使用指定的炮收尾。

AutoCollect,自動收集。參數(shù)為字符串?dāng)?shù)組,用于指定需要收集的掉落物品。可以選擇陽光、幼苗、銀幣、金幣、鉆石(美中不足的是無法收集巧克力)。如不寫,則默認(rèn)全部收集。該函數(shù)需要在選卡完成后,逐波拆解前調(diào)用。舉例:

IceSpots,自動存冰。第一個參數(shù)為 (y, x)?坐標(biāo)數(shù)組,每一個坐標(biāo)對應(yīng)一個存冰位。程序會按照坐標(biāo)先后順序依次嘗試存冰,所以需要把安全的位置寫在前面,危險的位置寫在后面。第二個參數(shù)為本輪的存冰數(shù)量,累計存冰達到該值時會退出存冰,后面即使寒冰菇的 CD?拖滿了也不會繼續(xù)存冰了。為了保證冰平衡,每次選卡后的存冰數(shù)量應(yīng)該和點冰數(shù)量保持一致。舉例:

Coffee,點冰。沒有參數(shù)。程序會掃描?IceSpots?里指定的存冰位,然后反向點冰,先點危險的冰,后點安全的冰。如果你在 IceSpots?里指定了 (4, 6) 和 (3, 6)?兩個存冰位,那么當(dāng)你調(diào)用 Coffee()?點冰時,會先嘗試點 (3, 6)?位置的冰,(3, 6)?處無冰時再嘗試 (4, 6)。

注意:夜晚場景下寒冰菇無法像白天一樣休息,請不要調(diào)用?IceSpots?和 Coffee?函數(shù)來存冰或點冰,請使用 Card?函數(shù)手動放置藍冰、白冰卡片。

4. 示例程序——PE 神之六炮

PE 神之六炮的原陣如下:

這里我做了微調(diào):

這樣調(diào)整的原因是,核殺小偷時,如果小偷偷 5 列向日葵,那必須要用 8 列核彈才能消滅,不能用 9 列核彈;微調(diào)后,向日葵進入傘葉的保護范圍,核彈可以放在 9 列了。

此陣使用 ch5 的雙冰變奏運行,軌道為:I-PP | I-PP | PP (15, 15, 6)。由于年度舞王(相比于經(jīng)典的 MJ 舞王)對前置炮的威脅大大降低,因此這里微調(diào)節(jié)奏,加速波延長至 7.25s 確保不漏撐桿,減速波提速至 14.5s 減少海豚對水路南瓜的啃食。

現(xiàn)在,我們在 E:\pvzscripts?倉庫目錄下新建一個 pe-god6p.py?的腳本文件,用于運行 PE 神之六炮。腳本代碼如下:

首先,我定義了一個函數(shù) ZombieCounts,它用于獲得指定名字的僵尸在每一波中出現(xiàn)的數(shù)量,結(jié)果用一個長度為 21?的數(shù)組表示。其中第 0?格空置,第 1~20?格記錄指定的僵尸在這一波里出現(xiàn)了多少只。舉例,調(diào)用 ZombieCounts("紅眼")[10]?可以得到第 10?波紅眼的數(shù)量。

另外,我還定義了一個函數(shù)叫 AliveTiaoTiao,用于檢測場上存活的跳跳。由于游戲機制問題,一對玉米炮炸 2、5 路時,1 路在空中的跳跳有概率躲過玉米炮的攻擊(在空中的跳跳相當(dāng)于 0 路跳跳,沒有 z 軸,y 軸來湊)。若改為炸 1、5 路,則又會漏掉 3 路的僵尸。所以我這里寫了一個額外的函數(shù)用于防御這些漏網(wǎng)之魚。這個函數(shù)的代碼參考了庫里的示例程序【信息讀取.py】中的代碼,并修改了一些內(nèi)存地址值。它的用途是,調(diào)用時檢測場上是否有存活的 1 路跳跳,并返回存活的數(shù)量。

然后腳本就開始運行了。首先看第一行代碼:

pao 這個量用于指示接下來要發(fā)幾號炮。神之六炮場上有 6 門炮,按 0~5 編號。按照所在列數(shù)排序,0~1 號炮為水路底線的兩門炮,2~3 號炮為水路位于向日葵右方的兩門炮,4~5 號炮為岸上的兩門炮。后續(xù)的代碼里,每發(fā)射一對炮,就將該值 +2 并對 6 取余,以確保該指針始終指向下一次發(fā)射的炮的編號。

接下來的六行代碼:

讀取了冰車、白眼、紅眼、小偷、鐵桶、跳跳這些特殊僵尸每波各出現(xiàn)多少只,以便將來運行腳本時能對癥下藥。

然后是選卡、開啟自動收集、開啟自動存冰這三步曲:

第二行代碼原先應(yīng)該寫成 AutoCollect() 的,但是這個框架里自帶的自動收集 API 非常難用,時常會發(fā)生錯誤點擊的事情。為了提高穩(wěn)定性,這里改為寫內(nèi)存打開游戲內(nèi)置的自動收集。向 0x004352f2 寫入 0xeb 這一個字節(jié)即可。

重頭戲來了,逐波拆解。直接一條 for?語句,進入?w = 1~20?的循環(huán)。注意 Python?里的區(qū)間是左閉右開的,所以用了 range(1, 21),意思是 1 ≤ w < 21。

如果是小波,則阻塞到 -195 的相對時間;如果是大波,則阻塞到 +5 的相對時間。內(nèi)置的 Prejudge 函數(shù)有?bug:大波阻塞時,如果阻塞的時間小于 0,則有概率拋出【到 xxx 的相對時間已過去,當(dāng)前相對時間 0】的異常,原因不明。由于該腳本里,大波不需要預(yù)判炸或預(yù)判冰,所以這里改為阻塞到 +5 的相對時間以對抗此 bug。

接下來有兩個 if...elif...else 塊,第一個塊用來書寫正常運陣時的操作,第二個塊用來書寫第 9、19 和 20?波的收尾操作。首先看第一個 if...elif...else 塊:

這里我先列一個表格來表示一下每波執(zhí)行的操作,以及波長:

可以看到,有三種不同類型的波:尾數(shù)是 1、2、5、8 的波為常規(guī)快波,尾數(shù)是 3、4、6、7、9 的波為常規(guī)慢波(其中尾數(shù)為 9 的波需要額外做中場拖尾處理),最后尾數(shù)為 0 的波是兩個旗幟波,需要做一些特殊操作。

我們首先處理常規(guī)快波。由于每一波都有 2s 的準(zhǔn)備時間,所以波長 7.25s 的快波需要確保 5.25s 時間炮要落地。而炮的飛行時間為 3.73s,這里取 0.25 的倍數(shù),視為 3.75s。我們需要在 5.25 - 3.75s 的時刻發(fā)炮。

然后開始處理常規(guī)慢波。首先當(dāng)然要炮落地點冰,激活預(yù)判冰。然后,和快波是類似的道理,慢波波長為 14.5s,因此在到達 12.5 - 3.75s 的時刻發(fā)一對炮。

慢波發(fā)完炮后需要執(zhí)行一些特殊操作。水路有 6 個南瓜,其中靠右的 4 個會受到海豚及關(guān)底的珊瑚三人組的啃食。因此南瓜需要定期維護。這里我們選擇在第 4、7、14、17 波依次補充 (4, 7)、(4, 6)、(3, 7)、(3, 6) 的南瓜,因此,要補充的南瓜的縱坐標(biāo) = 4 - 波次的十位數(shù),橫坐標(biāo) = 8 - 波次的個位數(shù)除以 3 的商。

最后是旗幟波。第 10 波無論是否有小偷都使用核彈(為了防止冰透支),第 20 波由于已經(jīng)到關(guān)底,盡量正常發(fā)炮收尾,僅當(dāng)有小偷出現(xiàn)時才使用核彈。咖啡豆喚醒核彈需花費 1.99s,取 2s。喚醒核彈后,核彈還需再花 1s 時間生效。而核彈波的波長是 7.5s,因此需要確保在本波相對時間 5.5s 時核彈生效。因此核彈和咖啡豆的種植時間為 5.5 - 3s,發(fā)炮時間則為 5.5 - 3.75s。第 10 波紅眼的密度會變大,如果一對炮沒有激活刷新,則需要在 (1, 9) 補種櫻桃炸彈確保刷新。

常規(guī)運陣時要做的事就這么多。然后開始處理第 9、19、20 波的收尾。

首先是第 9、19 波的首尾。開始收尾前,我們要處理一種特殊情況——夾零。根據(jù)游戲設(shè)定,出現(xiàn)紅字的時機是【第 9、19 波出生的僵尸(除伴舞外)均被消滅】,而不是【場上現(xiàn)存的僵尸均被消滅】。所以此時會出現(xiàn)一個詭異的情況:出怪里有紅眼,第 8 波正常刷出了紅眼,第 9 波卻又沒刷出紅眼(白眼也沒刷出),然后第 9 波一對炮下去,(第 8 波的)紅眼還在場上,還沒完成中場收尾呢,紅字就直接跳出來了。這種現(xiàn)象在 PvZ 里的術(shù)語叫做【夾零】。對于鍵控腳本來說,如果沒有夾零預(yù)案,則容易造成災(zāi)難性的后果。

如果你看到了類似于下面這樣的畫面,就說明第 9/19 波時出現(xiàn)了紅眼夾零:

紅眼還在場上,其他僵尸的灰還沒化掉,就出現(xiàn)了紅字,說明第 9/19 波紅眼夾零

明明有紅眼,但是節(jié)奏卻被加快了。如果不對夾零做特殊處理,原先不會受到威脅的炮可能反而會受到威脅。

由于第 9/19 波紅眼夾零,第 8/18 波的紅眼只挨了兩炮,存活時間較長,已經(jīng)開始對前置炮舉錘

在本陣?yán)?,紅眼夾零所帶來的后果除了前置炮受到的威脅變大外,還有一點:如果場上同時有冰車,則會因為節(jié)奏突然加快導(dǎo)致冰道無法及時融化,進而影響第 10 波櫻桃炸彈的使用。因此,我們對紅眼夾零的額外處理主要有兩點:一是燒冰道,二是出紅字時仍然需要按照節(jié)奏發(fā)炮,而不能傻傻等下一波僵尸出來。

首先看燒冰道:

然后開始說怎么收尾,包括常規(guī)拖收尾以及夾零收尾。收尾波里,需要發(fā)射的炮數(shù)量跟當(dāng)前出怪相關(guān),如果是極速關(guān)(或紅白夾零),一對炮足以刷新;如果是白眼關(guān),則需要兩對炮;而如果是紅眼關(guān),則三對炮后,還需要曾哥和冰瓜輸出幾下才能掛掉。所以這幾波我們不能把發(fā)炮對數(shù)寫死,而要不斷讀取戰(zhàn)場狀態(tài)動態(tài)決定接下來的行為。

為此,我導(dǎo)入了 pvz.extra?包里的 wave_countdown?和 huge_wave_countdown 函數(shù),用于讀取當(dāng)前波次的倒計時和大波倒計時。根據(jù)游戲設(shè)定,常規(guī)波的僵尸被清掉一半左右時,倒計時會強制置為 200,以便 2s?后刷新下一波怪。第 9?和 19?波有點特殊,一定要本波的僵尸全部清除后才會強制重置倒計時。重置后倒計時會變?yōu)?200,然后當(dāng)?shù)褂嫊r到達 4 后,轉(zhuǎn)為大波倒計時。第 20?波則更特殊,只要有僵尸在場上,不論是不是本波的,都必須要清除才會重置倒計時。因此在第 9、19?波時,我們可以通過不斷讀取刷新倒計時來檢查有沒有觸發(fā)紅字。

大致的意思就是,i 從 23 開始倒計時到 0,每過 1s 就將 i 減去 1。期間不斷讀取刷新倒計時,若仍大于 4,說明當(dāng)前波的僵尸還沒有清理干凈。特殊地,紅眼夾零時,常規(guī)倒計時只是個障眼法,需要繼續(xù)數(shù)到大波倒計時到達 300 以下。如果沒有紅眼,則 i 數(shù)到 0 時發(fā)一對炮;如果有紅眼,則 i 數(shù)到 8 和 0 的時候各發(fā)一對炮。另外,如果本波有跳跳,則在 i 數(shù)到 7 時調(diào)用 AliveTiaoTiao() 函數(shù)檢查有沒有漏炸的 1 路跳跳。若有,則立刻在 (1, 2) 放置窩瓜清理漏網(wǎng)之魚。

注意那句跳炮判定。這和第 20 波的終場收尾有關(guān)系,我會在終場收尾的部分再回過頭來解釋為什么這里要跳炮。現(xiàn)在請看第 20?波的收尾:

如果第 20 波有紅眼,拖 15s 和 23s 后各發(fā)一對炮就 over,邊路的紅眼被炸 3 炮后就是個大號路障,放到陣型里面去給 2 列曾哥收掉。如果有白眼或鐵桶,則至多拖 23s 后就必須消滅,不能將它們放入陣型內(nèi)部(單曾收不掉)。而如果剩下的是普僵、路障等,則不做額外處理,這些僵尸即使放入陣型內(nèi)部也是沒有關(guān)系的,2 列單曾可以完收。

注意白眼鐵桶的這個分支,我調(diào)用了 UpdatePaoList 這個函數(shù)更新了一下炮列表,確保收尾時用的是 (2, 5), (5, 5) 這一對岸路炮。如果用別的炮收尾,比如用 (3, 1), (4, 1) 這對水路炮,那么下次選卡時運行腳本必炸。因為腳本總是會以 (3, 1), (4, 1) 這一對水路炮起始,不會智能選擇別的炮。

然后,回過頭來解釋一下之前埋的那個跳炮坑。由于這里必須要以?(2, 5), (5, 5) 這一對岸路炮來收尾,那么就不能再以這對炮首發(fā)了(只拖 23s 沒有裝填完畢)。而如果第 19 波以 (3, 4), (4, 4) 這一對水路炮收尾的話,本波就必然以?(2, 5), (5, 5) 首發(fā)。因此第 19 波收尾時判定,如果指針指到了 2,且沒有紅眼,那么說明即將以?(3, 4), (4,?4) 收尾,產(chǎn)生矛盾。此時強行跳過 2 門炮,解決矛盾。

寫完后,我們在命令提示符中執(zhí)行以下命令切換到 E:\pvzscripts?目錄

然后打開 Steam?版 pvz,切換到泳池?zé)o盡模式,并執(zhí)行

就可以看到腳本的演示效果了。這里放一個現(xiàn)成的錄屏。請切換到 P2 觀看。

5. 一個復(fù)雜點的例子——RE 底線四炮

首先,我們要打開游戲里的隱藏頁面(Limbo Page)以便能進入屋頂無盡。請首先在 E:\pvzscripts 下新建一個 limbo.py 的 Python 腳本文件,并寫下以下代碼:

我們在命令提示符中執(zhí)行以下命令切換到 E:\pvzscripts?目錄

然后打開 Steam 版 pvz,在命令行里執(zhí)行

就解鎖了隱藏頁面。此時我們回到游戲窗口,點擊 SURVIVAL,然后我們驚喜地發(fā)現(xiàn)窗口的最下方多了一排【PAGE 0 | PAGE 1 | Limbo Page | PAGE 3】的小字:

這時候我們點擊 Limbo Page,就能看到一堆隱藏的小游戲,以及剩余四個沒有展示出來的無盡場地:

以上我們說明了怎么找到隱藏的無盡場地。那么,現(xiàn)在我們來看一個屋頂?shù)臉O簡炮陣:

屋頂沒有礦工舞王,因為相比于草地,屋頂?shù)耐咂@然不適合做這些事。與此對應(yīng)地,屋頂?shù)牡拙€炮少了礦工這個極大的威脅,只剩投籃車一個威脅。

另外還有一點要注意,在英文原版里,受上界之風(fēng) bug 的影響,底線兩炮沒法全收五行僵尸,只能收掉其中四行,以及剩下一行的大體積僵尸。而年度版(Steam 版)修復(fù)了這個 bug,這也使得本陣成為一個年度專屬陣。我們現(xiàn)在就要用我們這個適配了 Steam 版的鍵控框架給這個陣寫一個鍵控腳本。

本陣只有四門炮,同時每行都會刷出的冰車極大地限制了櫻桃、核彈等灰燼武器的使用,因此本陣選用 ch4* 雙冰變奏的運陣節(jié)奏:I-PP | I-PP (19, 19)。本陣的最大威脅是投籃車,而在完美預(yù)判冰下,讓投籃車不出手的極限波長為 19.29s。取 19s 波長既可略微提升對紅眼的壓制力,又不像 18s 波長那樣有較大的冰透支壓力,是綜合性能最好的波長。

現(xiàn)在,我們在 E:\pvzscripts?倉庫目錄下新建一個 re-base4p.py?的腳本文件,用于運行 RE?底線四炮。腳本代碼如下:

首先,ZombieCounts 函數(shù)的定義和 PE 神六里的那個定義一模一樣,不再說明。

接下來的 MyPao 是我對 Pao 函數(shù)的二次封裝,每發(fā)射一對炮,就記錄該對炮上次發(fā)射的相對時間(由 GameClock() 函數(shù)獲得的游戲內(nèi)部時鐘)。記錄時間是為了在收尾階段能夠【等待炮裝填完畢后立刻發(fā)射】,省去計算發(fā)射時間的過程。

SeedIsColding 和 DianCai 這兩個函數(shù)是為第 20 波加臨時傘彈走空降兵,以及拿花盆來墊紅眼巨人服務(wù)的,這里就不詳細解讀了,感興趣的讀者可以嘗試自己去解讀。

現(xiàn)在開始運行腳本。首先前兩行:

其中 pao 是一個指針,它的值為 0 時表示下一次發(fā)射的是左上角的這對炮;它的值是 1 時表示下一次發(fā)射的是左下角的這對炮。clk 數(shù)組則分別用于記錄這兩對炮前一次發(fā)射的時間。這兩個量的值會在調(diào)用 MyPao 函數(shù)時被自動修改。接下來第三行:

game_over 這個量是用來跟 DianCai 函數(shù)進行通訊的,這里我們只需要知道第 20 波收尾完成后,這個值會變?yōu)?True 即可。

接下來的五行代碼:

讀取了冰車、白眼、紅眼、小偷、鐵桶這些特殊僵尸每波各出現(xiàn)多少只,以便將來運行腳本時能對癥下藥。

然后是選卡、開啟自動收集、開啟自動存冰這三步曲:

開始逐波拆解。直接一條 for?語句,進入?w = 1~20?的循環(huán)。

如果是小波,則阻塞到 -195 的相對時間;如果是大波,則阻塞到 +5 的相對時間。內(nèi)置的 Prejudge 函數(shù)有 bug:大波阻塞時,如果阻塞的時間小于 0,則有概率拋出【到 xxx 的相對時間已過去,當(dāng)前相對時間 0】的異常,原因不明。由于該腳本里,大波不需要預(yù)判炸或預(yù)判冰,所以這里改為阻塞到 +5 的相對時間以對抗此 bug。

接下來有兩個 if...elif...else 塊,第一個塊用來書寫正常運陣時的操作,第二個塊用來書寫第 20?波的收尾操作。首先看第一個 if...elif...else 塊:

同樣,列表格來說明每一波的操作及波長:

為了減少用冰壓力,20 波怪里有 3 波是純加速、不點冰的,依次是第 1 波?6s 核彈,第 2 波?7.45s 一對炮和第 10 波 6.7s 一對炮。

第 1 波核彈代奏,所有怪出生后 0.25s 在 (2, 9) 依次放置花盆、核彈、咖啡豆。白天的核彈需要由咖啡豆喚醒,0.25~2.24s 的時間里是脆弱的,可能會被走得快的梯子僵尸啃兩口,但是沒有關(guān)系。2.24s~3.24s 的時間里核彈開始蓄力,蓄力階段免疫一切啃食傷害,直到 3.24s 時核彈爆炸。

第 2 波直接發(fā)一對炮。加速波籃球車最快于 5.47s 時出手,所以炮必須在?5.46s 或更早時落地。

這里需要注意,我們這次沒有像 PE 神六那樣把點冰操作 Coffee() 寫在冰波分支的開頭,而是寫在上一波的末尾。因為前者相當(dāng)于刷新后 1s 點冰,但是這樣的點冰在 ch4 節(jié)奏下已經(jīng)壓制不住籃球車了(PE 神六的 ch5 節(jié)奏仍可以壓制),我們需要進一步提早點冰時間,需要讓寒冰菇在下一波怪刷新后 0.1s 內(nèi)生效。這里我們需要牢牢記住一個定式:當(dāng)你于 x-375 時刻發(fā)炮,讓炮在 x-2?時刻落地時,若你在 x-90 時刻點冰,則可確保下一波怪刷新后 0.1s 內(nèi)生效

然后是常規(guī)冰波,也就是上半場的第 3~8 波,下半場的第 11~18 波:

由于預(yù)判冰已經(jīng)在前一波炮落地前點過了,所以這里就不用在開頭調(diào)用 Coffee() 了。直接阻塞到 1700-375 的相對時間發(fā)炮,落點的 x 坐標(biāo)取 8.5(為了同時炸到走得快的冰車和走得慢的巨人)。再阻塞到 1700-90 的相對時間點下一波的預(yù)判冰。

如果當(dāng)前波是第 12/14/16/18 波,則自動補坐標(biāo)為 (1, 3) / (2, 3) / (4, 3) / (5,3) 的南瓜。

然后是收尾冰波:

到了這一步,我們要未雨綢繆,先在腦子里想好怎么完成收尾工作。我們來簡單推演一下:

  1. 紅眼需要 3 個灰燼 + 1 個路障的自然輸出才能收掉,但是我們在這一波里只有兩對炮可用。因此引入核彈:本波有紅眼時,用兩對炮 + 一個核彈收尾。

  2. 如果沒有紅眼,但有白眼或鐵桶這樣的硬家伙的話,是不是用兩對炮就可以了呢?答案是否定的。第二對炮發(fā)出后出現(xiàn) 7.45s 的紅字,然后第 10 波的波長是 6.7s,第 11 波的波長是 19s,這時候我們在第 9 波用掉的炮裝填完畢了嗎?7.45 + 6.7 + 19 = 33.15 < 34.75,離裝填完畢還差 1.6s!因此第 9 波是萬萬不能用炮收尾的,即使是白眼,也只能用一對炮 + 一個核彈,這樣的組合來收尾。

那么,如果有冰車造冰道,不讓我放核彈怎么辦?用辣椒燒它丫的!刷出怪后,如果檢測到本波有冰車,且上波紅眼、本波紅眼、本波白眼、本波鐵桶至少有其一的話,在 (3, 4) 放辣椒。這里直接借用空置的存冰位來放辣椒,就不用額外放花盆了。

接下來我們等待到當(dāng)前炮裝填完畢:

當(dāng)前炮的上一次發(fā)射時間記錄在 clk[pao] 這個全局變量里(由 MyPao 函數(shù)改寫),那么下一次可發(fā)射的時間顯然是 clk[pao] + 3475。這里取了 clk[pao] + 3480,增加穩(wěn)定性。那么,我們用發(fā)射時間減去當(dāng)前時間,就得到了仍需等待的時間:clk[pao] + 3480 - GameClock()。如果仍需等待的時間大于 0,那么就等待這么多時間讓炮裝填完畢。

到了發(fā)射的時候,我們需要根據(jù)出怪種類的不同,選擇不同的炸點。如果本波有巨人,就炸 8.5 列,不用留其他僵尸,只留巨人拖尾即可;如果本波沒有巨人,就炸 8.2 列,把跑得快、威脅大的僵尸(橄欖、冰車、小丑、投籃)炸了,留下一些路障、鐵桶、撐桿啥的烏龜來拖尾。

注意這里 y 坐標(biāo)選的是 1、4 而不是 2、4,因為收尾階段要避免中路重疊火力。想象一下:第 9 波只有中路有一只白眼,然后你炸 2、4 路,白眼因為重疊火力被瞬殺了,直接出紅字,你的炮都還沒裝填好,GG!

現(xiàn)在開始收尾。如果是第 19 波,則啟動墊材線程,于 (2, 6)、(4, 6) 放臨時傘彈走接下來的空降兵,再于 (1, 6)、(5, 6) 放裸花盆。

只有上波紅眼、本波紅眼、本波白眼、本波鐵桶至少有其一的時候,才需要放核彈。其余情況就讓冰瓜自然拍死就 OK 了。

倒計時數(shù) 19s,期間不停讀取刷新倒計時。19s 后,若刷新倒計時仍大于 200,則(本波無紅)補一枚核彈,或(本波有紅)一對炮 + 一枚核彈。

到了中場旗幟波,需要考慮冰殺小偷的事。小偷落地時間為 375~394,停留 3s 后離開,離開時間為 675~694。顯然小偷的可冰殺時間為最早 395,最晚 674。

中場時我選擇了相對時間 97 發(fā)炮,相對時間 375 點冰。計算可知,炮于 97 + 373 = 470 時刻生效,2s 后于相對時間 670 刷出下一波怪。而 375 點冰時,冰于咖啡豆放下后 299 生效,冰生效時間為 375 + 299 = 674,也就是說,這個冰既充當(dāng)了第 11 波預(yù)判冰的作用(刷新后 0.04s 生效),又充當(dāng)了冰殺小偷的作用,一冰兩用,效率非常高!

點冰后在 (3, 4) 補南瓜套那是順帶的。

關(guān)底,常規(guī)操作很簡單,在 545 - 375 時刻發(fā)炮,如果有小偷則在 100 點冰,冰于 100 + 299 = 399 生效,略晚于最早可冰時間。同時補 (3, 5) 的南瓜。

第二個 if...elif?塊寫的是關(guān)底收尾邏輯。這里又分成了兩個分支:有紅收尾和無紅收尾。

有紅收尾:第一對炮發(fā)射后,等待 0.7s 發(fā)射第二對炮。如果第二對炮沒有裝填好,就等待裝填好后再發(fā)射。

第三對炮則是固定用左下角那對來發(fā)射。如果本波沒有冰殺小偷,則于第一對炮發(fā)射完畢后 34.8s 發(fā)射第三對炮;如果本波冰殺了小偷,為了緩解冰透支問題,改為第一對炮發(fā)射后 39.8s 發(fā)射第三對炮。

無紅收尾:比較簡單,第一對炮發(fā)射完畢后,等待 34.8s 發(fā)射左下角的那對炮。如果冰殺了小偷,多等 5s。

寫完后,我們在命令提示符中執(zhí)行以下命令切換到 E:\pvzscripts?目錄

然后打開 Steam?版 pvz,切換到屋頂無盡模式,并執(zhí)行

就可以看到腳本的演示效果了。這里放一個現(xiàn)成的錄屏。請切換到 P3?觀看。


植物大戰(zhàn)僵尸 Steam 版鍵控指南的評論 (共 條)

分享到微博請遵守國家法律
通海县| 田阳县| 错那县| 靖西县| 出国| 呼伦贝尔市| 阿荣旗| 鄂尔多斯市| 怀安县| 凌海市| 吐鲁番市| 财经| 翼城县| 虞城县| 高雄县| 哈尔滨市| 阿拉善右旗| 朝阳市| 若尔盖县| 乐亭县| 承德市| 古蔺县| 临汾市| 成安县| 镶黄旗| 水富县| 通河县| 成都市| 七台河市| 垣曲县| 海阳市| 海南省| 仙游县| 贵阳市| 平塘县| 历史| 岳阳市| 昭通市| 图木舒克市| 禄劝| 吉隆县|