Common Commands Cookbook - Alchyr/ModTemplate-StS2 GitHub Wiki
This page contains a series of code snippets for common patterns that are used within the game's code for different actions. They are inspired by base game code examples and attempt to follow their coding conventions. Compose these together to create the actions you want for your content.
Here are a Model methods you will commonly use and their general use case
| Action | Notes |
|---|---|
| OnPlay | |
| OnUpgrade | |
| BeforeCardPlayed | |
| AfterCardPlayed | |
| AfterSideTurnStart | |
| AfterTurnEnd | |
| AfterCardDrawn | |
| AfterPowerAmountChanged |
Models in STS2 will often have base values defined within CanonicalVars. When set up properly, these act as the base source of truth for your Models. You can define as many variables for a Model as you like.
In the sections below, there will be a code snippet for their associated variable Eg.
protected override IEnumerable<DynamicVar> CanonicalVars => [ new DamageVar(6, ValueProp.Move)), new PowerVar<StrengthPower>(1) ];If you have a variable that does not fall into any existing category, you can use the generic var types to make custom your own custom variable.
protected override IEnumerable<DynamicVar> CanonicalVars =>
[
//via nameof
new IntVar(nameof(LostFingers), 2),
//via hardcoded strings
new IntVar("NumberOfGremlins", 5),
new StringVar("TamerName", "Franklin"),
new BoolVar("IsStinky", true)
];Note: All variables follow this same mechanic underlyingly, but are easier to call their Dynamic Variable.
TODO
The game calculates modified values through a Model's DynamicVars. These are the values you want to use when performing commands.
You can invoke most generic dynamic vars via their built in getter Eg. DynamicVars.Damage.BaseValue. If they do not have a getter you have to fetch them by name Eg. DynamicVars[nameof(StrengthLossPower)].BaseValue.
Commands are the building blocks of your Model's actions.
Depending on your card's command, you will also have to change your card's TargetType.
Variable:
new DamageVar(6, ValueProp.Move))Commmand:
await DamageCmd.Attack(DynamicVars.Damage.BaseValue)
.FromCard(this)
.Targeting(play.Target)
.WithHitFx("vfx/vfx_attack_slash")
.Execute(choiceContext);Commmand:
await DamageCmd.Attack(DynamicVars.Damage.BaseValue)
.FromCard(this)
.TargetingAllOpponents(CombatState)
.WithHitFx("vfx/vfx_attack_blunt", null, "heavy_attack.mp3")
.Execute(choiceContext);Commmand:
await DamageCmd.Attack(DynamicVars.Damage.BaseValue)
.FromCard(this)
.TargetingRandomOpponents(CombatState)
.WithHitFx("vfx/vfx_attack_slash")
.Execute(choiceContext);Variables:
new DamageVar(6, ValueProp.Move)),
new RepeatVar(3),Command:
await DamageCmd.Attack(DynamicVars.Damage.BaseValue)
.FromCard(this)
.WithHitCount(DynamicVars.Repeat.IntValue)
.WithHitFx("vfx/vfx_attack_slash")
.Execute(choiceContext);Variable:
new BlockVar(6, ValueProp.Move))Command:
await CreatureCmd.GainBlock(Owner.Creature, DynamicVars.Block, play);Powers, as in PowerModels are any buff or debuff that can be applied to a creature or player. They can represent buffs gained from Power cards, but also effects bestowed by Skill cards or by other other effects.
They require both a PowerType and a PowerStackType.
public class ExplosivesPower : CustomPowerModel
{
public override PowerType Type => PowerType.Buff; //Buff or Debuff
public override PowerStackType StackType => PowerStackType.Counter; //None, Counter, Single
}Variable:
Power canonical variables are unique in that they require a type parameter.
new PowerVar<ExplosivesPower>(1),
new PowerVar<ExplosivesPower>("FollowupExplosivePower", 1),Command:
Similarly to their Variables, applying powers also needs a type parameter.
await PowerCmd.Apply<ExplosivesPower>(
new ThrowingPlayerChoiceContext(),
Owner.Creature,
DynamicVars[nameof(ExplosivesPower)].BaseValue,
Owner.Creature,
this);Commands:
await PowerCmd.Decrement(this);
await PowerCmd.Remove(this);
await PowerCmd.ModifyAmount(ctx, this, -1, null, null);
SetAmount(Amount - 6);Command:
CardCmd.Upgrade(card);Command: TODO
Command:
await CardCmd.AutoPlay(choiceContext, card, target);Command:
await CardCmd.Exhaust(choiceContext, card);Command:
await CardCmd.Discard(choiceContext, card);Command:
CardCmd.ApplyKeyword(card, CardKeyword.Ethereal);Command:
CardCmd.Enchant<Sharp>(card, amount);Command:
var prefs = new CardSelectorPrefs(SelectionScreenPrompt, 1);
var card = (await CardSelectCmd.FromSimpleGrid(
choiceContext,
PileType.Discard.GetPile(Owner).Cards,
Owner,
prefs)).FirstOrDefault();Command:
var prefs = new CardSelectorPrefs(SelectionScreenPrompt, 1);
var selected = (await CardSelectCmd.FromHand(choiceContext, Owner, prefs, null, this))
.FirstOrDefault();Command:
var prefs = new CardSelectorPrefs(SelectionScreenPrompt, 0, DynamicVars.Cards.IntValue);
var selected = await CardSelectCmd.FromHand(choiceContext, Owner, prefs,
c => c.IsTransformable, this);Variable:
new CardsVar(1)Command:
await CardPileCmd.Draw(choiceContext, DynamicVars.Cards.BaseValue, Owner);Command:
await CardPileCmd.Add(card, PileType.Hand);
await CardPileCmd.Add(card, PileType.Draw, CardPilePosition.Top);
await CardPileCmd.Add(card, PileType.Draw, CardPilePosition.Random, this);Command:
CardCmd.PreviewCardPileAdd(await CardPileCmd.AddGeneratedCardToCombat(
card, PileType.Draw, Owner, CardPilePosition.Random));Command:
CardCmd.PreviewCardPileAdd(await CardPileCmd.AddGeneratedCardsToCombat(
cards, PileType.Hand, Owner.Player));Command:
await CardPileCmd.AddToCombatAndPreview<Debris>(Owner.Creature, PileType.Hand, 4, Owner);Command:
await CardPileCmd.AddCursesToDeck(Enumerable.Repeat(ModelDb.Card<Guilty>(), 1), Owner);