121 - API文档汉化 - 自定义卡牌费用系统
Updated a week ago自定义卡牌费用系统
自定义卡牌费用
在《邪恶冥刻》中,玩家将遇到四种不同的资源类型:血祭(Blood)、骨头/兽骨(Bones)、能量(Energy)以及玛珂/宝石(Mox/Gems)。
这些资源用于支付具有相应费用类型的卡牌,大多数卡牌至少需要消耗其中一种资源。
通过本API,您可以创建需要消耗多种资源类型的卡牌,为模组开发者提供更丰富的创作空间。
当基础费用类型无法满足需求时,或者您希望创建全新费用类型时,API提供了通过CardCostManager创建基础卡牌费用的多种方式。
社区补丁中包含一个TestCost类,实现了本页将介绍的诸多功能。
创建新费用类型
要创建新的卡牌费用类型,需要新建一个继承自API中CustomCardCost类的子类。
CustomCardCost类已提供确保费用正常运作的基础功能。
请注意:API不提供自定义费用资源的支持,您需要通过补丁自行实现相关逻辑。
创建费用类后,需向API注册该类型。完整示例如下:
// 为简化示例,本费用类型复制了能量费用的逻辑
public class MyCardCost : CustomCardCost
{
// 必需字段,应与注册时传入API的名称一致
public override string CostName => "TestCost";
// 检查当前卡牌是否满足费用支付条件
public override bool CostSatisfied(int cardCost, PlayableCard card)
{
// 判断玩家能量是否足够支付(需考虑基础能量消耗)
return cardCost <= (ResourcesManager.Instance.PlayerEnergy - card.EnergyCost);
}
// 当费用不足时显示的提示文本
public override string CostUnsatisfiedHint(int cardCost, PlayableCard card)
{
return $"Eat your greens aby. {card.Info.DisplayedNameLocalized}";
}
// 卡牌成功打出后触发的逻辑
// 如需消耗资源,应在此处实现相关逻辑
public override IEnumerator OnPlayed(int cardCost, PlayableCard card)
{
// 扣除玩家对应数量的能量
yield return ResourcesManager.Instance.SpendEnergy(cardCost);
}
}
public void AddCost()
{
// 注册时需要提供两个Func:分别用于获取3D模式下的费用纹理和第二章的像素纹理
// 若费用仅限特定章节使用,可为对应Func传入null
CardCostManager.Register("api", "TestCost", typeof(TestCost), TextureMethod, PixelTextureMethod);
}
注册费用时,可直接使用静态方法代替委托创建。
这些方法必须包含三个参数类型:int, CardInfo, PlayableCard;并返回Texture2D对象。
int参数表示卡牌费用值,CardInfo和PlayableCard参数表示当前检查的卡牌信息(PlayableCard可能为null)
public static Texture2D TextureMethod(int cardCost, CardInfo info, PlayableCard card)
{
return TextureHelper.GetImageAsTexture($"myCost_{cardCost}");
}
public static Texture2D PixelTextureMethod(int cardCost, CardInfo info, PlayableCard card)
{
return TextureHelper.GetImageAsTexture($"myCost_pixel_{cardCost}");
// 如需API自动处理堆叠数字显示,可提供7x8纹理如下:
// return Part2CardCostRender.CombineIconAndCount(cardCost, TextureHelper.GetImageAsTexture("myCost_pixel_7x8"));
}
不同章节的费用纹理规格要求:
第一章需64x28像素;第二章最大30x8像素(或7x8像素,见上);第三章无固定尺寸,但建议不超过300x78像素。
负费用、费用等级与第二回合可打出机制
API允许定义是否允许负费用值,默认情况下为false(负值会被视为0,但实际赋值不变)。
可通过SetCanBeNegative方法修改该设置,或直接修改CanBeNegative字段。
费用等级(Cost Tier)是表示卡牌消耗量的整数值,每种费用类型有其独立的计算公式。
例如骨头费用的计算公式为(数量/3)向下取整。
默认情况下,自定义费用不计入卡牌费用等级计算,可通过SetCostTier方法定义计算函数。
public static void Init()
{
FullCardCost cost = CardCostManager.Register(InscryptionAPIPlugin.ModGUID, "TestCost", typeof(TestCost), Texture3D, TexturePixel);
cost.SetCostTier(CostTier);
}
public static int CostTier(int amount)
{
return Mathf.FloorToInt(amount / 2f);
}
公平手牌机制是游戏核心玩法之一:
战斗开始时,系统会保证玩家至少获得一张可立即打出的卡牌,以及一张第二回合可打出的卡牌。
默认情况下,带有自定义费用的卡牌在检查第二回合可打出性时总是返回2(即使实际不可打出)。
需通过设置CanBePlayedByTurn2WithHand函数修正该行为:
public static void Init()
{
CardCostManager.FullCardCost fullCardCost = CardCostManager.Register(InscryptionAPIPlugin.ModGUID, "TestCost", typeof(TestCost), Texture3D, TexturePixel);
fullCardCost.SetCanBePlayedByTurn2WithHand(CanBePlayed);
}
// amount参数为卡牌费用值,hand参数为玩家手牌列表
public static bool CanBePlayed(int amount, CardInfo card, List<CardInfo> hand)
{
// TestCost仿照能量费用逻辑,费用值≤2即可第二回合打出
return amount <= 2;
}
费用选择节点
如需在第一章的费用选择节点出现您的自定义费用,API提供如下实现方式:
public static void Init()
{
CardCostManager.FullCardCost fullCardCost = CardCostManager.Register(InscryptionAPIPlugin.ModGUID, "TestCost", typeof(TestCost), Texture3D, TexturePixel);
// 设为true将为该费用分配专属ResourceType,使其出现在选择节点
// rewardBack参数为选择节点显示的纹理(125x190像素)
fullCardCost.SetFoundAtChoiceNodes(isChoice: true, rewardBack: (Texture2D)ResourceBank.Get<Texture>("Art/Cards/RewardBacks/card_rewardback_bird"));
}
您可能对ResourceType产生疑问——简言之,该值用于决定向玩家提供哪些费用选择,以及玩家可获得哪些卡牌。
不同费用数量
除ResourceType外,系统还会根据资源数量进一步区分选项。
默认情况下,选择节点的自定义费用将提供所有有效卡牌(无论所需资源数量)。
这与骨头和能量的处理方式类似,但若您希望像血祭费用那样提供1/2/3不同等级的选项,可使用如下变体方法:
public static void Init()
{
CardCostManager.FullCardCost fullCardCost = CardCostManager.Register(InscryptionAPIPlugin.ModGUID, "TestCost", typeof(TestCost), Texture3D, TexturePixel);
// 此版本方法需提供Func而非Texture2D
// 该Func用于确定每个有效数量对应的奖励背景纹理
fullCardCost.SetFoundAtChoiceNodes(isChoice: true, rewardBackFunc: GetRewardBack, 1, 2, 4);
// 后续费用选择节点可能提供消耗1/2/4个TestCost的卡牌选项
}
也可直接设置FullCardCost对象的ChoiceAmounts字段(需传入包含数量值的整型数组)。
费用类型分组
当添加多个自定义费用时,您可能希望将它们归入同一选择组。
无论出于关联性考虑,还是避免挤占其他选项空间,API均支持此功能。
private void Example()
{
FullCardCost cost = CardCostManager.Register(...);
cost.ChoiceAmounts = new int[] { 1, 4, 7 };
}
分组自定义费用必须共享相同的ResourceType值:
public static void Init()
{
// 首先标记首个费用为可选
CardCostManager.FullCardCost fullCardCost = CardCostManager.Register(InscryptionAPIPlugin.ModGUID, "TestCost", typeof(TestCost), Texture3D, TexturePixel);
fullCardCost.SetFoundAtChoiceNodes(isChoice: true, rewardBack: (Texture2D)ResourceBank.Get<Texture>("Art/Cards/RewardBacks/card_rewardback_bird"));
// 后续费用需设置相同ResourceType
CardCostManager.FullCardCost fullCardCost2 = CardCostManager.Register(InscryptionAPIPlugin.ModGUID, "TestCost2", typeof(TestCost2), Texture3D2, TexturePixel2);
fullCardCost2.ResourceType = fullCardCost.ResourceType;
}
注意:分组费用与多数量费用不兼容,若分组中某费用已定义多种可选数量,这些设置将被忽略。
为卡牌添加费用
通过API的扩展属性系统为卡牌添加自定义费用,并可通过相同方式访问。
为明确用途,API提供以下扩展方法设置CardInfo的自定义费用:
public void AddCard()
{
CardInfo info = CardManager.New("myMod", "custom_card", "Card", 1, 1);
info.SetCustomCost("TestCost", 1);
int cost = info.GetCustomCost("Test") // 返回1
}
死亡卡牌的自定义费用
死亡卡牌(Death cards)本质上是一种应用到特定应用到模板卡牌上的修改了的原版卡牌。
因此直接添加扩展属性无效(属性会应用于该卡牌的所有副本)。
如需创建使用自定义费用的死亡卡牌,必须新建CardInfo并添加属性。
幸运的是,API的DeathCardManager可处理此流程。
CreateCustomDeathCard()方法将返回代表自定义死亡卡牌的CardInfo对象,并根据提供的CardModificationInfo设置名称、属性等数据。
private void AddCustomDeathCard()
{
CardModificationInfo deathCardMod = new CardModificationInfo(2, 2)
.SetNameReplacement("Mabel").SetSingletonId("wstl_mabel")
.SetBonesCost(2).AddAbilities(Ability.SplitStrike)
.SetDeathCardPortrait(CompositeFigurine.FigurineType.SettlerWoman, 5, 2)
.AddCustomCostId("CustomCost", 1);
// 可将新建的死亡卡牌加入默认列表
DeathCardManager.AddDefaultDeathCard(deathCardMod);
}
进阶功能
旧版API中,自定义费用(至少其纹理)由社区补丁处理。
这些方法仍可使用,本节将介绍旧版纹理添加方式。
using InscryptionCommunityPatch.Card;
using InscryptionAPI.Helpers;
Part1CardCostRender.UpdateCardCost += delegate(CardInfo card, List<Texture2D> costs)
{
int myCustomCost = card.GetExtensionPropertyAsInt("myCustomCardCost") ?? 0; // GetExtensionPropertyAsInt可能返回null
if (myCustomCost > 0)
costs.Add(TextureHelper.GetImageAsTexture($"custom_cost_{myCustomCost}.png"));
}
为第二章添加自定义费用有两种主要方式:
using InscryptionCommunityPatch.Card;
using InscryptionAPI.Helpers;
// 方式一:使用7x8像素图标(由API处理堆叠数字)
Part2CardCostRender.UpdateCardCost += delegate(CardInfo card, List<Texture2D> costs)
{
int myCustomCost = card.GetExtensionPropertyAsInt("myCustomCardCost_pixel") ?? 0;
if (myCustomCost > 0)
{
Texture2D customCostTexture = TextureHelper.GetImageAsTexture($"custom_cost_pixel.png");
costs.Add(Part2CardCostRender.CombineIconAndCount(myCustomCost, customCostTexture));
}
}
// 方式二:使用30x8像素纹理(自主控制显示效果)
Part2CardCostRender.UpdateCardCost += delegate(CardInfo card, List<Texture2D> costs)
{
int myCustomCost = card.GetExtensionPropertyAsInt("myCustomCardCost_pixel") ?? 0;
if (myCustomCost > 0)
{
costs.Add(TextureHelper.GetImageAsTexture($"custom_cost_{myCustomCost}_pixel.png"));
}
}
第三章实现
第三章的卡牌费用显示更为复杂,因其采用3D对象而非平面纹理。
这意味着您对费用外观拥有更高控制权。
提供两个自定义费用事件:
Part3CardCostRender.UpdateCardCostSimple
:快速提供/修改卡牌费用纹理
Part3CardCostRender.UpdateCardCostComplex
:支持直接修改GameObject,可附加任意组件
基础费用实现
若只需显示基础费用,准备代表单个费用单位的图标即可(如货币费用使用"$"符号)。
图标尺寸无严格限制,但显示区域为300x73像素,因此高度不应超过73像素,宽度越大则显示数量越少。
Part3CardCostRender.GetIconifiedCostTexture
辅助方法接收图标和费用值,生成包含默认纹理(albedo)和自发光纹理(emissive)的纹理对。
当300x73区域可容纳全部图标时显示重复图标,否则显示7段数码管样式。
以货币费用为例:
- 费用3显示为"$$$"
- 费用10显示为"$ x10"
实现代码如下:
using InscryptionCommunityPatch.Card;
using InscryptionAPI.Helpers;
MyIconTexture = TextureHelper.GetImageAsTexture("cost_icon.png");
Part3CardCostRender.UpdateCardCostSimple += delegate(CardInfo card, List<Part3CardCostRender.CustomCostRenderInfo> costs)
{
int myCustomCost = card.GetExtensionPropertyAsInt("myCustomCardCost");
costs.add(new ("MyCustomCost", Part3CardCostRender.GetIconifiedCostTexture(MyIconTexture, myCustomCost)));
}
高级费用实现
通过UpdateCardCostComplex
事件可直接修改GameObject,但需要掌握Unity引擎知识:
using InscryptionCommunityPatch.Card;
using InscryptionAPI.Helpers;
using UnityEngine;
Part3CardCostRender.UpdateCardCostSimple += delegate(CardInfo card, List<Part3CardCostRender.CustomCostRenderInfo> costs)
{
costs.add(new ("MyCustomCost"));
}
Part3CardCostRender.UpdateCardCostComplex += delegate(CardInfo card, List<Part3CardCostRender.CustomCostRenderInfo> costs)
{
GameObject costObject = costs.Find(c => c.name.Equals("MyCustomCost")).CostContainer;
// 可直接操作costObject添加组件
}
Pages
- 0 - 邪恶冥刻模组简体中文语言包Wiki
- 100 - API文档汉化 - 首页
- 110 - API文档汉化 - 入门指南
- 120 - API文档汉化 - 卡牌
- 121 - API文档汉化 - 自定义卡牌费用系统
- 122 - API文档汉化 - 会说话的卡牌
- 123 - API文档汉化 - 自定义毛皮
- 130 - API文档汉化 - 能力
- 131 - API文档汉化 - 自定义触发器
- 132 - API文档汉化 - 自定义狙击逻辑
- 133 - API文档汉化 - 伤害护盾行为
- 134 - API文档汉化 - 卡槽修改功能
- 135 - API文档汉化 - 触发器与执行顺序
- 140 - API文档汉化 - 自定义/扩展属性
- 150 - API文档汉化 - 扬升(凯茜模组)
- 160 - API文档汉化 - 地图与遭遇战
- 170 - API文档汉化 - 对手
- 180 - API文档汉化 - 图腾
- 190 - API文档汉化 - 道具
- 1A0 - API文档汉化 - 规则书
- 1A1 - API文档汉化 - 添加自定义页面
- 1A2 - API文档汉化 - 添加文本重定向功能
- 1B0 - API文档汉化 - 本地化
- 1B0 - API文档汉化 - 声音
- 1B0 - API文档汉化 - 资产包
- 1C0 - API文档汉化 - 其他特性
- 200 - JSONLoader文档汉化 - Wiki
- 201 - JSONLoader文档汉化 - 枚举值