本系列为面向新人的PVZ键控炮阵教程,使用基于AvZ 1的Simple AvZ,讲解常见阵型的脚本编写思路。
开始阅读前,请确保你已根据此教程正确安装AvZ 1与Simple AvZ插件。本篇还会用到拦截计算器(点此下载)和复用计算器(包含在炮阵工具大礼包内)。如果你第一次接触无尽模式,推荐先阅读新轨道读本(至少读完前四篇)。
索引
上一篇:#03 双冰变奏能吃吗?
下一篇:#05 不种太阳花的艺术
阵型简介
本篇要讲解的阵型是 ME.神之六炮:

布阵码:LI43NBRTWVNPTDL6ye0msgJEX3rmJrKYzX8FEASANFZUQaFBvVE=
主体节奏为C7u(参考视频):
C7u: IP-PP | IP-PP | PA | N | PP
两个减速波各约18s,后面三个加速波加起来也约18s。所以你可以将这个节奏理解为3个18s片段,每个片段里含有3炮,其中1炮较早生效(热过渡里的P,或PA里的P);另外2炮较晚生效。
那么,冰波波长该取多少呢?
查询大黄表(https://pvz.tools/wiki/#%E5%83%B5%E5%B0%B8),可知原速梯子最早562啃7列炮:

假设使用10冰(I3时机),则梯子啃炮时机变为:
562 * 2 + 400 - 10 = 1514
由于游戏设定,原速僵尸每4cs结算啃食,减速僵尸每8cs结算啃食,因此1520(8的倍数)前都不会真正啃到,波长可以取到 1520 - 1 + 200 = 1719。
接下来,让我们用复用计算器验证在该波长下,节奏循环是否成立。
下载并打开复用计算器(包含在炮阵工具大礼包内),切换至“循环”工作簿:

绿色为输入区域,红色为输出数据,黄色为文字说明。在左表输入循环节奏每波波长与炮生效时机,并在中表填写炮数,即可计算循环是否成立、实际炮数需求等。
按1719冰波波长填入C7u循环,并填写炮数为“6”,然后双击任意单元格:

这里加速波填401,是因为天台巨人“肾虚”,激活过早时可能把小鬼丢到67列炮屁股上。无冰瓜时326、有冰瓜时427或更晚激活才能确保100%安全。这里虽然有冰瓜,但数量不多,401尚可接受。
得到“炮数需求=8”,“复用窗口=3422”的结果,意味着此循环节奏需8门炮才能成立;如果只有6门炮,则最吃紧的一门炮的复用间隔为3422cs。
炮的恢复时长为3475cs,因此只要复用窗口<3475,循环必定不成立。“炮数需求=8”也表达了类似的意思。
emm这是怎么回事呢?查看右表:

这个表看着最复杂,但其实是最好用的。中间一列是你填写的所有炮的生效时机;左列、右列分别是此炮距上次使用、距下次使用的复用间隔。
以第一行为例:

其含义为:我们填写的第一门炮生效时机为301,距(前一次循环中)上次使用间隔3522cs,距下次使用间隔3538cs。由于两边都≥3475cs,此炮无复用问题。
回看左表:

红框处就是前面说的“第一门炮”,生效时机确实是301。其下次使用是在高亮处,间隔为 1719 - 301 + 1719 + 401 = 3538,与计算结果相符。
回到右表:

所谓的“复用成立”,其实就是这里左列、右列里所有的值都≥3475。上文说的“复用窗口”(最吃紧的一门炮的复用间隔),就是这里左列、右列里的最小值。
因此,复用窗口的数值(3422)必定会出现在右表里,如图中红框所示。
借由此,我们可以知道是倒数第三个炮有复用问题:

为解决此问题,我们试着将核波延至746(无视投篮的最大波长):

复用窗口增至3438,但循环仍不成立。
新的问题出在最后两个炮(箭头处)。这些炮下次使用为第二冰波激活炮,无法延后,因此必须提前本次的生效时机,由401提前至401 - (3475 - 3438) = 364。
最终得到:

炮数需求=6,复用窗口=3475,循环成立。
规划相位
本阵无视小偷,但仍有以下限制:
w10不应为冰波,因为要用窝瓜消延迟,更适合用在加速波
由于只有2个冰瓜,1级IO并不稳定,需尽可能避免四连加速波
总结如下:

第一冰波可以只取其激活炮,作为加速波用在w10;第二冰波则不适合,因为会形成四连加速波;A波、N波会和窝瓜抢花盆,皆不适合w10。
沿用“上下f同相位规划法”,综合考虑后确定w10对应第一冰波(的加速波版本)。如此,w1/w11对应第二冰波;w9/w19对应第三加速波,此后有两个冰可用,轻松收尾。
排列逐波
本阵C7u节奏由3个18s片段组成,每个片段3门炮。但别忘了:这里是天台,有蛋疼的炮序问题,6炮需拆成【2风1平】与【1风2平】两个炮组。由于两个炮组交替使用,每次循环同一波次对应的炮组是不同的。
打开复用计算器,切换至“逐波”工作簿:

其颜色交互逻辑和“循环”页相同。
由于先前已确定相位,我们可以直接将“循环”页的数值复制进来:

显然,炮数需求仍为6,复用依旧成立。
和“循环”页不同,“逐波”页列出了每波具体生效了哪些炮,适合更精细的规划。
这里将w1复制到了第二行,是因为上下f相位相同,第一行预留给w10。这样规划下f时可沿用此表。
假设起手先用【2风1平】炮组,将所有风炮依次加粗,得到下图:

由此,我们就掌握了每波具体用哪些炮,极大地方便了之后的脚本编写。
书写循环主体
先写减速波:
// I&#39;-PP
for (auto w : waves(1, 6, 11, 16)) {
feng.P(301, 3, 9.3);
if (w == 1 || w == 11) {
feng.P(leng1 - 200, 2, 8.4);
} else {
ping.P(leng1 - 200, 1, 8.4);
}
ping.P(now, 4, 8.4);
if (w == 1) {
C(10, {POT, M_ICE}, 1, 1);
}
}
// I-PP
for (auto w : waves(5, 15)) {
I(10, 1, 1);
feng.P(301, 3, 9.3);
feng.P(leng1 - 200, 2, 8.4);
ping.P(now, 4, 8.4);
M_I(after(210), 1, 1);
}
热过渡炮炸9.3列是为了避免风炮漏下行冰车。激活炮的8.4列可由拦截计算器(见#03中的说明)得到:

然后书写加速波:
// PA
for (auto w : waves(2, 7, 12, 17)) {
if (w != 17) {
C(395, keep(0), {POT, CHERRY}, 4, 9);
feng.P(401, 2, 9);
} else {
C(395, keep(0), {POT, CHERRY}, 3, 9);
C(395, JALA, 1, 1);
feng.P(401, 5, 9);
}
}
// N
for (auto w : waves(3, 8, 13, 18)) {
int row = 3;
int col = 9;
if (w == 8) {
row = 2;
} else if (w == 13) {
row = 4;
}
EnsureExist({ZOMBONI, row});
C(exact(leng2 - 200 + 100), {POT, DOOM}, row, col);
}
// PP
for (auto w : waves(4, 14)) {
ping.PP(364, 9);
}
N波统一采用车底炸,使用EnsureExist确保冰车出现在核武路。这主要是为了演示方便。实际冲关时,可以根据场况判断该行是否有冰车/投篮会碾爆花盆,依此决定是否车底炸。
考虑无法车底炸,即花盆于核波446放置的情况。花盆CD为751,因此上一次放置最晚为樱波 446 - 751 + 601 = 296,即樱桃最晚只能396生效。
Simple AvZ 统一使用炮等效时机,实际396生效的樱桃等价于395生效的炮,故语句中使用的时机为395。
由于2-9、3-9、4-9三坑轮流使用,樱桃波时只有即将放核的坑位确保可用,因此樱桃位置需和核坑同步(2-9核前一波用2-9樱,依此类推)。用3-9樱时在1路补辣椒。
处理w10
401激活+窝瓜消延迟即可:
for (auto w : waves(10)) {
ping.PP(401);
C(225, until(401), {POT, SQUASH}, 1, 9);
M_I(601 + 10, 1, 1);
C(after(1), PUFF, 1, 1);
RM(1421, PUFF, 1, 1);
}
1-1用小喷垫蹦极。受冻时,蹦极最晚 394 + 300 + 600 + 63*2 = 1420 离开(最晚394落下,停留300,冰冻最多600,偷取63受减速*2)。故这里在1421时铲。
RM支持指定要铲的类型,若该位置不存在此类型植物,则什么都不做。
处理w9/w19收尾
回到复用计算器“逐波”页:

最下面一行列出了下波恢复的炮的时机。w9/w19对应第三加速波,因此可以401并炸,然后再710炸中三路,延迟210冰(I3)。填入表格:

可以看到“下波恢复”也随之更新了。据此可确定:1928并炸、2529炸中三路、3876并炸,即可完成收尾,边路残血红交给曾哥喷杀。
for (auto w : waves(9, 19)) {
EnsureExist({GIGA, 1});
feng.P(401, 2, 9);
ping.P(now, 4, 9);
feng.P(710, 3, 9.3);
I(after(210), 1, 1);
ping.PP(1928, 8.5);
feng.P(2529, 3, 9.3);
feng.P(3876, 2, 7);
ping.P(now, 4, 7);
}
为演示方便,这里使用EnsureExist确保红眼出在边路。实际上即使边路无红,2529就清场了,之后仍有足够炮可用。此处略去详细证明过程,有兴趣的读者可以自行在复用计算器里探索(参考数据:w9/w19清场945cs后刷下一波)。
使用EnsureExist前,必须考虑非理想情况下的对策。这种对策不一定在视频里有所体现,但它是阵型论证完整性的重要一环。
w20常规收尾即可,不做额外讲解。
完整代码
// ME.神之六炮
#include &quot;SimpleAvZ/lib.h&quot;
int leng1 = 1719;
int leng2 = 746;
CobOperator feng(1);
CobOperator ping(6);
void Script()
{
OpenMultipleEffective(&#39;C&#39;);
SelectCards({ICE, M_ICE, POT, DOOM, CHERRY, JALA, SQUASH, PUFF, KERNEL, COB}); // 带炮套件,被小鬼爆菊后可以补
SetZombies({PJ_0, CG_3, BC_12, XC_15, QQ_16, TT_18, FT_21, TL_22, BY_23, HY_32});
// I&#39;-PP
for (auto w : waves(1, 6, 11, 16)) {
feng.P(301, 3, 9.3);
if (w == 1 || w == 11) {
feng.P(leng1 - 200, 2, 8.4);
} else {
ping.P(leng1 - 200, 1, 8.4);
}
ping.P(now, 4, 8.4);
if (w == 1) {
C(10, {POT, M_ICE}, 1, 1);
}
}
// PA
for (auto w : waves(2, 7, 12, 17)) {
if (w == 2 || w == 17) {
C(395, keep(0), {POT, CHERRY}, 2, 9);
feng.P(401, 5, 9);
} else if (w == 7) {
C(395, keep(0), {POT, CHERRY}, 3, 9);
C(395, JALA, 1, 1);
feng.P(401, 5, 9);
} else {
C(395, keep(0), {POT, CHERRY}, 4, 9);
feng.P(401, 2, 9);
}
}
// N
for (auto w : waves(3, 8, 13, 18)) {
int row = 2, col = 9;
if (w == 8) {
row = 3;
} else if (w == 13) {
row = 4;
}
EnsureExist({ZOMBONI, row});
C(exact(leng2 - 200 + 100), {POT, DOOM}, row, col);
}
// PP
for (auto w : waves(4, 14)) {
ping.PP(364, 9);
}
// I-PP
for (auto w : waves(5, 15)) {
I(10, 1, 1);
feng.P(301, 3, 9.3);
feng.P(leng1 - 200, 2, 8.4);
ping.P(now, 4, 8.4);
M_I(after(210), 1, 1);
}
for (auto w : waves(9, 19)) {
EnsureExist({GIGA, 1});
feng.P(401, 2, 9);
ping.P(now, 4, 9);
feng.P(710, 3, 9.3);
I(after(210), 1, 1);
ping.PP(1928, 8.5);
feng.P(2529, 3, 9.3);
feng.P(3876, 2, 7);
ping.P(now, 4, 7);
}
for (auto w : waves(10)) {
ping.PP(401);
C(225, until(401), {POT, SQUASH}, 1, 9);
M_I(601 + 10, 1, 1);
C(after(1), PUFF, 1, 1);
RM(1421, PUFF, 1, 1);
}
for (auto w : waves(20)) {
M_I(409, 1, 1);
C(after(1), PUFF, 1, 1);
RM(1421, PUFF, 1, 1);
ping.P(594, 3, 7);
feng.P(409, 2, 9);
ping.P(now, 4, 9);
feng.P(1421, 3, 9);
C(after(210), keep(0), {POT, ICE}, 1, 1);
feng.P(2106, 2, 8.5);
ping.P(now, 4, 8.5);
feng.P(3884, 2, 7);
ping.P(now, 4, 7);
}
}
以上代码可以转载、传播、修改,但请注明出处。
索引
上一篇:#03 双冰变奏能吃吗?
下一篇:#05 不种太阳花的艺术