131 - API文档汉化 - 自定义触发器

Updated 6 days ago

自定义触发器

为赋予模组制作者更精细的能力触发控制权,本API提供了大量可通过接口添加到能力或其他触发器接收器的自定义触发器。

同时新增了ExtendedAbilityBehaviour类,该类已预置多个上述接口的实现。

关于API添加的所有自定义触发器的完整信息,请参阅文档。若需了解这些触发器的调用顺序,请查阅相关文章。

被动属性增益

属性图标是一种独特的能力类型,用于修改卡牌的attack或health。蚂蚁是应用属性图标的典型范例,通过本API您可创建自定义版本的属性图标。

实现方式为:继承IPassiveAttackBuff或IPassiveHealthBuff接口并实现必要方法。其中GetPassiveAttackBuff(PlayableCard target)GetPassiveAttackBuff(PlayableCard target)分别用于计算attack和health增益值。

战斗中,游戏会遍历场上所有卡牌来检查是否应获得增益——此处的target即指当前被检查的卡牌,而指代携带该自定义印记的卡牌。

您需自行编写判断逻辑以确定哪些卡牌应获得增益及增益数值。例如若仅希望增益作用于携带该印记的卡牌,则需验证target是否等于这张牌本身。

重要提示:必须谨慎处理GetPassiveAttackBuffs和GetPassiveHealthBuffs中的逻辑复杂度。 这些方法会为每个能力实例在每帧调用!! 不当处理可能导致游戏性能显著下降!

牌面朝下时触发

通过API可为能力添加自定义属性,其方法与为卡牌添加自定义属性相同。

API还允许控制能力触发器是否在卡牌面朝下时激活。 需重写TriggerWhenFaceDown方法并返回true。

另有2个布尔值可重写以获得更精细的触发控制:ShouldTriggerWhenFaceDown用于控制原版触发器是否激活;ShouldTriggerCustomWhenFaceDown则控制自定义触发器是否激活。

修改攻击目标卡槽

需重写RespondsToGetOpposingSlots返回true(与所有RespondsToXXX重写相同,可设为条件判断),然后重写GetOpposingSlots返回该能力期望卡牌攻击的卡槽列表。 若需覆盖默认卡槽(卡牌正对面的卡槽)而非新增卡槽,则需重写RemoveDefaultAttackSlot返回true。

修改攻击发起卡槽

注意名称相似性,此部分实际与前述内容不同。 通过IGetAttackingSlots接口,可修改每回合发起攻击的卡牌卡槽。

例如创建完全禁止攻击的印记:

public class DontAttack : AbilityBehaviour, IGetAttackingSlots
{
    public bool RespondsToGetAttackingSlots(bool playerIsAttacker, List<CardSlot> originalSlots, List<CardSlot> currentSlots)
    {
        return true;
    }
    // 返回待添加到攻击卡槽列表的卡槽
    public List<CardSlot> GetAttackingSlots(bool playerIsAttacker, List<CardSlot> originalSlots, List<CardSlot> currentSlots)
    {
        // 可直接修改currentSlots
        currentSlots.Remove(base.Card.Slot);

        // 若不添加新卡槽,返回new()或null
        return new();
    }

    // 用于多印记修改攻击卡槽时确定触发顺序(如其他模组)
    // 触发器按优先级从高到低排序
    public int TriggerPriority(bool playerIsAttacker, List<CardSlot> originalSlots)
    {
        // 此例中不关心其他印记是否重新添加该卡槽
        // 若需控制可返回更低数值如-1000等
        return 0;
    }
}

关键点在于:卡槽列表是未过滤的,意味着某些卡槽可能不会出现在最终列表中。

游戏会自动移除空卡槽及被0 Power卡牌占据的卡槽; 若添加此类卡槽将被移除。

这也意味着检查卡槽时不能假设其上有卡牌。

public List<CardSlot> GetAttackingSlots(bool playerIsAttacker, List<CardSlot> originalSlots, List<CardSlot> currentSlots)
{
    // 此写法将引发错误
    currentSlots.RemoveAll(slot => slot.Card.HasAbility(Ability.Sharp));

    // 此写法安全
    currentSlots.RemoveAll(slot => slot.Card != null && slot.Card.HasAbility(Ability.Sharp));

    return new();
}

修改承受伤害

通过IModifyDamageTaken接口可增减卡牌受攻击时的伤害值。 伤害值不会低于0;若计算结果为负值,API将在所有计算完成后将其设为0。

public class ReduceDamageByOne : AbilityBehaviour, IModifyDamageTaken
{
    public static Ability ability;
    public override Ability Ability => ability;

    public bool RespondsToModifyDamageTaken(PlayableCard target, int damage, PlayableCard attacker, int originalDamage)
    {
        // 将此卡承受伤害减1
        if (base.Card == target && damage > 0)
            return attacker == null;

        return false;
    }

    public int OnModifyDamageTaken(PlayableCard target, int damage, PlayableCard attacker, int originalDamage)
    {
        damage--;
        return damage; // 亦可单行返回damage - 1
    }

    public int TriggerPriority(PlayableCard target, int damage, PlayableCard attacker) => 0;
}

主动能力扩展功能

API新增ExtendedActivatedAbilityBehaviour类,为创建主动能力提供额外功能。

该类改变了主动能力的实现方式。举例而言,若继承原版ActivatedAbilityBehaviour,需重写BonesCost来设置...骨头费用。而在ExtendedActivatedAbilityBehaviour中,该功能移至虚属性StartingBonesCost,BonesCost现用于追踪包含修改器在内的总费用。

简言之,设置初始费用需重写StartingBonesCost、StartingEnergyCost或StartingHealthCost。

另新增可重写的IEnumerator PostActivate()方法,在主代码体执行后触发。

血祭费用

其行为与出牌时的血祭费用完全相同,需牺牲己方场上其他卡牌来触发效果。

被牺牲卡牌的CardInfo和CardSlot将存储在字典currentSacrificedCardInfos中,其中CardInfo为键,CardSlot为值。若需在卡牌消失后操作牺牲卡牌可使用此字典,但请注意能力效果执行完毕后该字典会被清空。

public Dictionary<CardSlot, CardInfo> currentSacrificedCardInfos = new();

public override IEnumerator Activate()
{
    // 在原始卡槽重建被牺牲卡牌的副本
    foreach (KeyValuePair<CardSlot, CardInfo> valuePair in currentSacrificedCardInfos)
    {
        yield return Singleton<BoardManager>.Instance.CreateCardInSlot(valuePair.Value, valuePair.Key);
    }
}

生命费用

该机制易于理解,即设置主动能力需费用生命值来发动。 若费用值等于卡牌当前生命值,则该卡牌会死亡。

通过重写StartingHealthCost进行设置。

动态激活费用

使用ExtendedActivatedAbilityBehaviour可在战斗中改变主动能力的费用值。

通过重写OnActivateBonesCostMod、OnActivateEnergyCostMod、OnActivateBloodCostMod或OnActivateHealthCostMod,可使能力激活后的费用值递增。

public class ActivateRepulsive : ExtendedActivatedAbilityBehaviour
{
    public static Ability abiliy;
    public override Ability Ability => ability;
    
    public override int StartingBonesCost => 2;
    public override int OnActivateBonesCostMod => 1;
    
    public override IEnumerator Activate()
    {
        yield return this.Effect();
    }
}

若希望费用值呈指数增长,可持续修改这些修饰字段。

public override IEnumerator PostActivate();
{
    OnActivateBonesCostMod += 1;
}

对修改费用值的方式与时机基本没有限制。

public override IEnumerator OnUpkeep(bool playerUpkeep)
{
    bonesCostMod -= 1;
}

此代码将在每个回合开始时使骨头费用减1。

注意此处使用bonesCostMod而非OnActivateBonesCostMod:OnActivate...字段用于每次激活时的费用变更,而...CostMod字段用于其他情况。关键在于...CostMod字段会持续追踪当前费用修饰值。

bonesCostMod = 0;

此操作将重置当前骨头费用修饰值。

另需注意:不仅限于修改单一费用类型,可同时修改任意费用值,包括那些初始时非必须的激活条件。

状态追踪

随着卡牌激活费用的可变性,如何追踪新费用值成为问题。虽然您可能拥有惊人记忆力,但API提供了通过更新能力规则书描述来简化追踪的方式。

当右键点击卡牌能力图标时,API会获取当前激活费用并显示。此显示针对每张卡牌独立,无需担心多激活费用的追踪问题。

要指定规则书描述中需修改的部分,必须使用sigilcost:X标记,其中X为初始激活费用。

string rulebookDescription = "消耗[sigilcost:1骨头,1能量]执行操作,随后其激活费用增加1能量。"

AbilityManager.New(pluginGuid, rulebookName, rulebookDescription, typeof(T), "artpath.png");

触发OnResolveOnBoard

在第二章中,当打出具有主动能力的卡牌时会触发相关教程。为确保该代码运行,禁止重写OnResolveOnBoard(包括原版行为及此扩展版本)。

但存在替代方案:可重写新增的虚方法RespondsToPostResolveOnBoard()和OnPostResolveOnBoard()达到相同效果。

public override IEnumerator RespondsToPostResolveOnBoard()
{
    return true;
}

public override IEnumerator OnPostResolveOnBoard()
{
    // 在此处添加代码
}