从零开始键控炮阵 #04:排炮序的特殊技巧
Crescebdo
编辑于 2024年03月10日 13:10
收录于文集
共15篇

本系列为面向新人的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平】炮组,将所有风炮依次加粗,得到下图:

由此,我们就掌握了每波具体用哪些炮,极大地方便了之后的脚本编写。

 


书写循环主体

先写减速波:

代码块
C++
自动换行
复制代码
// I'-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中的说明)得到:

然后书写加速波:

代码块
C++
自动换行
复制代码
// 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激活+窝瓜消延迟即可:

代码块
C++
自动换行
复制代码
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并炸,即可完成收尾,边路残血红交给曾哥喷杀。

代码块
C++
自动换行
复制代码
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常规收尾即可,不做额外讲解。


完整代码

代码块
C++
自动换行
复制代码
// ME.神之六炮
#include "SimpleAvZ/lib.h"

int leng1 = 1719;
int leng2 = 746;

CobOperator feng(1);
CobOperator ping(6);

void Script()
{
    OpenMultipleEffective('C');
    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'-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 不种太阳花的艺术​