Cards must exist in a card pool. When creating a card using CustomCardModel, mark it with the [Pool] annotation to determine what pool to add it to. The [Pool] annotation can be inherited.
Using CustomCardModel is not required, as you can just mark your classes with the ICustomModel interface instead. However, if your class does not inherit CustomCardModel, you will need to add a default constructor that calls CustomContentDictionary.AddModel for the [Pool] annotation to function.
[Pool(typeof(IroncladCardPool))]
public class FancyStrike() : CustomCardModel(1, CardType.Attack, CardRarity.Basic, TargetType.AnyEnemy)
{
protected override HashSet<CardTag> CanonicalTags => [CardTag.Strike];
protected override IEnumerable<DynamicVar> CanonicalVars => [new DamageVar(77, ValueProp.Move)];
protected override async Task OnPlay(MegaCrit.Sts2.Core.GameActions.Multiplayer.PlayerChoiceContext choiceContext, MegaCrit.Sts2.Core.Entities.Cards.CardPlay play)
{
//CommonActions is a utility class provided by BaseLib to shorten some common card/relic effects.
await CommonActions.CardAttack(this, play.Target, vfx: "vfx/vfx_attack_slash").Execute(choiceContext);
//The equivalent basegame code is the following.
await DamageCmd.Attack(card.DynamicVars.Damage.BaseValue)
.FromCard(this)
.Targeting(cardPlay.Target)
.WithHitFx("vfx/vfx_attack_slash")
.Execute(choiceContext);
}
protected override void OnUpgrade()
{
DynamicVars.Damage.UpgradeValueBy(23m);
}
}
Orobas Ancient Cards
ITranscendenceCard should be implemented on a starter card that is upgraded by Orobas. Any ancient card within a character’s pool that is not a transcendence card can be chosen for Darv’s Dusty Tome.
Visuals
While a card’s appearance is normally determined by its pool, CustomCardModel allows cards to individually set their frame (background) texture and material by overriding CustomFrame and/or CreateCustomFrameMaterial.
CardModel Documentation
TODO
ConstructedCardModel
ConstructedCardModel is an alternative method of defining cards that can be used as a base class instead of CustomCardModel. Its setup is significantly different from basegame cards, so it is not recommended to use unless you are confident in your ability to translate basegame examples to another format.
Instead of overriding properties to set up the card, methods are called in the card’s constructor. The main advantage of this setup is that certain processes can be automated, such as adding tooltips for powers when a PowerVar/keyword is used, and simply having slightly shorter definitions in general for more complex cards.
This is a reimplementation of the above card as an example.
[Pool(typeof(IroncladCardPool))]
public class FancyStrike : CustomCardModel(1, CardType.Attack, CardRarity.Basic, TargetType.AnyEnemy)
{
public FancyStrike() : base(1, CardType.Attack, CardRarity.Basic, TargetType.AnyEnemy)
{
WithDamage(77, 23);
WithTags(CardTag.Strike);
}
protected override async Task OnPlay(MegaCrit.Sts2.Core.GameActions.Multiplayer.PlayerChoiceContext choiceContext, MegaCrit.Sts2.Core.Entities.Cards.CardPlay play)
{
//CommonActions is a utility class provided by BaseLib to shorten some common card/relic effects.
await CommonActions.CardAttack(this, play.Target, vfx: "vfx/vfx_attack_slash").Execute(choiceContext);
//The equivalent basegame code is the following.
await DamageCmd.Attack(card.DynamicVars.Damage.BaseValue)
.FromCard(this)
.Targeting(cardPlay.Target)
.WithHitFx("vfx/vfx_attack_slash")
.Execute(choiceContext);
}
}
While most of the methods should be relatively straightforward to understand by looking at the ConstructedCardModel class, WithTip has some additional things to note.
Tooltips for powers, cards, potions, and enchantments can be generated by passing their type directly to the method.
WithTip(typeof(StrikeIronclad))
Other valid parameter types are CardKeyword and StaticHoverTip enum values.