添加卡牌
以下示例默认你已经在Entry.Init()中启用了RitsuLib的自动注册,否则[RegisterCard]之类的attribute不会生效(详见第0章):
1 2 3 var assembly = Assembly.GetExecutingAssembly(); RitsuLibFramework.EnsureGodotScriptsRegistered(assembly, Logger); ModTypeDiscoveryHub.RegisterModAssembly(ModId, assembly);
代码 创建一个新的Cards文件夹方便管理,并创建新的cs文件,例如TestCard.cs。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 using MegaCrit.Sts2.Core.Commands;using MegaCrit.Sts2.Core.Entities.Cards;using MegaCrit.Sts2.Core.GameActions.Multiplayer;using MegaCrit.Sts2.Core.HoverTips;using MegaCrit.Sts2.Core.Localization.DynamicVars;using MegaCrit.Sts2.Core.Models.CardPools;using MegaCrit.Sts2.Core.Models.Cards;using MegaCrit.Sts2.Core.ValueProps;using STS2RitsuLib.Cards.DynamicVars;using STS2RitsuLib.Interop.AutoRegistration;using STS2RitsuLib.Keywords;using STS2RitsuLib.Scaffolding.Content;namespace Test.Scripts ; [RegisterCard(typeof(ColorlessCardPool)) ]public class TestCard : ModCardTemplate { private const int energyCost = 1 ; private const CardType type = CardType.Attack; private const CardRarity rarity = CardRarity.Common; private const TargetType targetType = TargetType.AnyEnemy; private const bool shouldShowInCardLibrary = true ; public override CardAssetProfile AssetProfile => new ( PortraitPath: $"res://Test/images/cards/{GetType().Name} .png" ); protected override IEnumerable<DynamicVar> CanonicalVars => [ new DamageVar(12 , ValueProp.Move) ]; public TestCard () : base (energyCost, type, rarity, targetType, shouldShowInCardLibrary ) { } protected override async Task OnPlay (PlayerChoiceContext choiceContext, CardPlay cardPlay ) { await DamageCmd.Attack(DynamicVars.Damage.BaseValue) .FromCard(this ) .Targeting(cardPlay.Target!) .Execute(choiceContext); } protected override void OnUpgrade () { DynamicVars.Damage.UpgradeValueBy(4 ); } }
[RegisterCard(typeof(ColorlessCardPool))]会把这张卡自动注册进指定卡池。这里是无色卡池。
[RegisterCharacterStarterCard(typeof(TestCharacter), 5)]会把它自动登记成该角色的起始卡组内容。如果你不是做起始卡,删掉这行即可。
CanonicalVars翻译是“规范值”,指卡牌的基础数值。添加一个DamageVar意为指定卡牌的基础伤害是多少,例如这里是12。
ValueProp表示数值的属性,例如ValueProp.Move表示是通过卡牌造成的伤害/格挡,ValueProp.Unpowered表示不受修正影响(如力量等),ValueProp.Unblockable表示伤害不可被格挡,ValueProp.SkipHurtAnim表示跳过受伤动画。这是一个bitflag类型的枚举,你可以进行组合,例如ValueProp.Unblockable | ValueProp.Unpowered,不可被格挡也不受修正影响。
尖塔2使用了async和await来控制效果逻辑顺序执行,比如选择一张牌时就一直await不让后续代码执行,和尖塔1的action类似的生态位。此处的OnPlay中写了一个造成单体伤害的指令。
想做什么样的卡牌,看原版代码哪张有类似的效果,参考即可。
继承ModCardTemplate而不是CardModel。
注意 :通过ritsulib添加卡牌,其id会变成{modid}_CARD_{原卡牌id},例如原始卡牌id为TEST_CARD,是TestCard的大写snake-case,最后变成TEST_CARD_TEST_CARD。
卡图 可以在AssetProfile变量里指定卡图路径:
1 2 3 public override CardAssetProfile AssetProfile => new ( PortraitPath: $"res://Test/images/cards/{GetType().Name} .png" );
如果你按这行代码写,文件名就对应Test/images/cards/TestCard.png。这里的res://Test/...是Godot资源路径,对应的是你的资源文件夹名字。
记得修改Test为你的modid。modId即为你{modId}.json中填写的。不是你的根目录,而是一个新文件夹。
卡图任意尺寸都可,且不需要裁剪,官方使用的尺寸是普通卡250x190,先古卡250x351。
如果你想统一管理卡图路径,也可以额外写一个抽象基类,例如TestCardModel.cs,然后其他卡牌类继承这个类即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 using MegaCrit.Sts2.Core.Entities.Cards;using STS2RitsuLib.Scaffolding.Content;namespace Test.Scripts ; [RegisterCard(typeof(TestCardPool), Inherit = true) ]public abstract class TestCardModel : ModCardTemplate { public override CardAssetProfile AssetProfile => new ( PortraitPath: $"res://RitsuTest/images/cards/{GetType().Name} .png" , FramePath: type switch { CardType.Attack => "res://RitsuTest/images/card_frame_attack.png" , CardType.Skill => "res://RitsuTest/images/card_frame_skill.png" , CardType.Power => "res://RitsuTest/images/card_frame_power.png" , _ => "" } ); public TestCardModel (int energyCost, CardType type, CardRarity rarity, TargetType targetType, bool shouldShowInCardLibrary ) : base (energyCost, type, rarity, targetType, shouldShowInCardLibrary ) { } }
文本 此外还需要本地化文件。创建一个{modId}/localization/{Language}/cards.json。
modId即为你{modId}.json中填写的。不是你的根目录,而是一个新文件夹。
Language可以写zhs表示简体中文。填写{CardId}.title(卡牌名)和{CardId}.description(卡牌描述):
通过ritsulib添加内容,其id会变成{modid}_{类别}_{原id}。例如这里的modid是TEST,类别是CARD。
1 2 3 4 { "TEST_CARD_TEST_CARD.title" : "测试卡牌" , "TEST_CARD_TEST_CARD.description" : "造成{Damage:diff()}点伤害。" }
{Damage:diff()}对应前面的DamageVar。
编译打包dll和pck后打开游戏。如果你在对应池子中看到卡牌说明成功了。如果没有任何卡牌(或者一张在左上角的卡牌)说明出问题了。
按~打开控制台输入card TEST_CARD_TEST_CARD获得这张卡。
最终项目参考 如果报错,回头看看。最终项目结构参考:
1 2 3 4 5 6 7 8 9 10 11 Test (你的项目文件夹) ├── Scripts (脚本文件夹随意组织) │ ├── TestCard.cs (或者套一层Cards文件夹) │ └── Entry.cs └── Test (不要忘了这一层文件夹,是你的modid) ├── images │ └── cards │ └── TestCard.png └── localization └── zhs └── cards.json