熊猫rrr

V1

2023/05/25阅读:13主题:默认主题

无名杀扩展学习-5:创建触发技(2)

上一篇文章我们创建了一个简单的触发技,今天我们将尝试创建一个稍微复杂一些的触发技,以此来熟悉无名杀中的一些对象和方法的使用。

代码编写

我们今天要编写的是一个名为“仁义”的技能,技能描述如下:

仁义:回合结束阶段,你可以弃置一张手牌,若弃置的手牌为红色,你回复1点体力;若为黑色则摸2张牌。

现在我们在extension.js中输入以下代码:

    extension_skills.renyi = {
        trigger: { player: 'phaseJieshuBegin' },
        frequent: false,
        filter: function (event) {
            if(player.countCards('h') == 0){
                return false;
            }
            if(player.countCards('h', {color: 'black'}) == 0 && player.hp == player.maxHp){
                return false;
            }
            return true;
        },
        content: function () {
            "step 0"
            player.chooseToDiscard('弃置1张红牌回复1点体力,弃置1张黑牌摸2张牌', 1, 'h')
                .set('ai'function (card) {
                    var value = get.value(card);
                    var color = get.color(card);
                    if (player.hp <= 2) {
                        if (color == 'red') {
                            value -= 3;
                        }
                    } else {
                        if (color == 'black') {
                            value -= 3;
                        }
                    }
                    return 10 - value;
                })

            "step 1"
            var card = result.cards[0];
            if (get.color(card) == 'red') {
                player.recover(1);
            } else if (get.color(card) == 'black') {
                player.draw(2);
            }
        },
        check: function (event, player) {
            var num = player.countCards('h'function (card) {
                return get.value(card) < 8;
            })
            return num > 0;
        }
    }

    extension_skill_translate.renyi = '仁义';
    extension_skill_translate.renyi_info = '回合结束阶段,你可以弃置一张手牌,若弃置的手牌为红色,你回复1点体力;若为黑色则摸2张牌。';

然后将桃园兄弟的技能改为仁义,就可以进游戏体验啦!

代码详解

可以看到,此技能的整体结构跟上一篇文章的“骁勇”是一样的。

trigger对象设置的是角色的结束阶段开始时:{player: 'phaseJieshuBegin'}

filter函数比“骁勇”的要复杂一些。我们需要在这里作一些判断。既然此技能需要先弃置一张手牌,那么我们首先要判断的自然是有没有手牌了,没有手牌的时候肯定不能发动啦。所以在filter函数中我们首先通过这个if语句来判断:

if(player.countCards('h') == 0){
    return false;
}

看名称就能知道,player.countCards就是根据特定条件来获取牌数的方法。它可以接收一个表示区域的字符串参数,来获取指定区域的牌数。区域字符串的含义见前面《创建视为技》的文章。在区域字符串之外,它还可以接收对象或者函数。当参数为对象时,它将按照对象中的属性来计算符合这些属性的牌的数量;当参数为函数时,它会把区域内的每一张牌作为参数来调用该函数,然后返回函数结果为true的数量。在上面的语句中,我们将字符串'h'作为参数,来获取角色当前的手牌数。当手牌数等于0时,filter函数就返回false,以阻止技能的发动。

我们在filter函数中的下一个部分就能看到player.countCards的另一中用法。在判断完是否有手牌之后,我们紧接着需要判断这样一种情况:如果角色的体力值是满的,而此时他手中只有红色牌而没有黑色牌,那么他也不能发动技能。因为弃置红色牌是回复1点体力,当他满体力时就没什么可回复的了。所以我们又加了这么个判断语句:

if(player.countCards('h', {color: 'black'}) == 0 && player.hp == player.maxHp){
    return false;
}

在这里,我们在区域字符串'h'之外,还加了一个对象作为参数来调用player.countCards,如上所述,我们需要在这个对象中通过设置各种属性来指明我们要获取什么样的牌。这些属性跟《创建视为技》这篇文章中提到的get系列方法很相似。我们用到的{color: 'black'}即表示查询黑色牌的数量,牌的其他属性参考《创建视为技》即可,这里就不再赘述了。

player.hpplayer.maxHp分别表示角色的当前体力值和最大体力值。player对象是无名杀中最重要的对象之一,它定义了角色的各种属性和方法。我们写扩展代码的时候会在各种地方都用到这个对象,所以,在下一篇文章中,我们将单独介绍这个player对象。

作完上面两个判断之后,剩下的情况应该是都能发动技能了,所以filter函数的最后返回一个true就行了。


下面我们再看content中的内容。在此技能的content中,有个很明显的特点是用到了诸如"step 0""step 1"等字符串。第一次看到这种写法的时候你可能会有些懵:这是js中的什么新语法吗?这些step都起什么作用呢?别急,下面我们来详细说明。

在游戏中,有很多情况是需要等待用户操作的。比如在仁义技能发动后,用户首先需要选择一张牌来进行弃置。那么代码运行到这里的时候,就需要中断了,等到用户弃置完之后,再根据他所弃置的牌执行下一步的代码,这种情况就要使用step标记了。step字符串在无名杀中起的是一个标记位的作用,无名杀在执行content函数时,如果遇到step字符串,就会将此函数转换为一个switch函数。比如我们仁义技能中的这个content函数,实际执行的时候会被转换成这样:

function anonymous(event, step, source, player, target, targets, card, cards, skill, forced, num, trigger, result, _status, lib, game, ui, get, ai) {
    if (event.step == 2) { 
        event.finish(); 
        return
    } 
    switch (step) {
        case 0:
            player.chooseToDiscard('弃置1张红牌回复1点体力,弃置1张黑牌摸2张牌', 1, 'h')
                .set('ai'function (card) {
                    var value = get.value(card);
                    var color = get.color(card);
                    if (player.hp <= 2) {
                        if (color == 'red') {
                            value -= 3;
                        }
                    } else {
                        if (color == 'black') {
                            value -= 3;
                        }
                    }
                    return 10 - value;
                })
            break
        case 1:
            var card = result.cards[0];
            if (get.color(card) == 'red') {
                player.recover(1);
            } else if (get.color(card) == 'black') {
                player.draw(2);
            }
    }
}

在技能发动后,这个函数首先在step0时被调用,每个用户操作行为完成之后就会将step这个变量进行递增,然后再次调用这个函数。函数根据step变量的值来执行switch块中的对应语句。因此,在需要等待用户操作的时候,只要用step来分步写就行啦!

此外,我们看转换后的函数,会发现它添加了一大堆的入参,所以我们在写content函数时,尽管content函数本身没有入参,但我们还是可以直接调用诸如eventplayerresult等对象哦!比如在step 1中,我们就调用了result对象。当角色操作完成之后,系统会将用户操作的结果放到result对象中传入转换后的函数。所以我们就可以通过result.cards来获取用户选择的所有牌。我们这里限定只能弃置1张牌,所以result.cards[0]就是被角色弃置的那张牌了。之后我们只要根据这张牌的颜色来让角色回复体力或者摸牌就可以了。


content中的第一个步骤中,我们调用了player.chooseToDiscard这个函数,顾名思义,这个函数是用来让当前角色选择弃置卡牌的。在这个函数后面可以紧跟一个set函数来定义ai的选择策略。凡是涉及到选择的函数,比如选择对象、选择选项等,都可以用此方法来定义ai。我们这里的set函数接收两个参数,一是固定的字符串'ai',第二个是以card为参数的函数,此函数的作用就是计算出每张牌的价值,然后将价值最高的那张表定为选择目标。比如在我们这个例子中,先通过get来获取牌本身的价值和颜色,然后进行判断:如果角色的体力值小于或等于2,那么我们应该更倾向于弃置红牌来回复体力,那么红色牌的价值要减去3,因为最终价值是用10减去计算后的牌价值,因此牌价值越小,它最后的使用价值(也就是被选为弃置对象的可能性)就越大;反之当角色体力值大于2时,我们更倾向于弃置黑牌来摸2张牌,那么就把黑色牌的价值减去3。这样就能使ai在不同的形势下作出更合理的选择。


最后的check函数中,我们也是作了个判断。首先获取手牌中牌价值小于8的牌的数量,如果此数量大于0,ai才会发动技能。这样ai就不会把【无中生有】这种高价值牌弃置掉了。


对比上一篇文章中的技能,我们本次的技能显然更为复杂了些,但是整体结构与上一篇的技能是一致的。我们只需要进一步熟悉诸如playerget等对象,一定就能编写出更多复杂的技能,加油!

分类:

前端

标签:

JavaScript

作者介绍

熊猫rrr
V1