On this page:
5.1 Where Can I Store Scenario Programs?
5.2 Bestiary Format by Example
5.2.1 Write Your Own Monster
5.2.2 Using Multiple Bestiaries
5.3 Bestiary Format Reference
5.4 Area-of-Effect Specification by Example
5.5 Area-of-Effect Specification Reference
5.6 Foe Specification by Example
5.7 Foe Specification Format Reference
5.8 Programming Loot

5 Programming a Scenario🔗

There are many elements of a scenario, such as which characters are playing at what level, what the loot is, and which foes are involved. This document describes how you can program these elements so that the Frosthaven Manager can load a whole scenario without asking you to input this information every time you play.

Monster information, including statistics and abilities is stored in a "bestiary." See Step 5: Select a Bestiary for how the bestiary is loaded into the game. We’ll cover how to write bestiaries in Bestiary Format by Example.

Instead of a bestiary, you can use a foe specification, which describes what monsters you’ll face in a scenario in addition to everything a bestiary describes. This can also be loaded into the game; see Step 5: Select a Bestiary. You can use all the features of bestiaries to define or import monsters and abilities, and there are extra features to specify exactly what foes you’ll face in the scenario. We’ll cover writing foe specifications starting in Foe Specification by Example.

A good pattern for personal organization is to use bestiaries to define a collection of monsters, and foe specifications to define scenario-specific foes by importing the bestiary.

You can also define a stickered set of loot cards for your scenario to avoid re-stickering cards each scenario. We’ll cover how to do so in Programming Loot.

Interested in contributing to Frosthaven Manager? Help me build editors to make creating custom bestiaries and scenarios easier!

5.1 Where Can I Store Scenario Programs?🔗

Simply: anywhere! Any plain-text file will do.

Note that some "text editors" are for rich text. Do not use Microsoft Word to edit plain-text files. On macOS, be careful with the TextEdit.app—it has options to edit rich-text format (RTF) files, which should be avoided when working with plain text.

Since the contents of the file will be a program in a Racket dialect, the traditional suffix is "rkt". So you might create a plain-text file named "my_bestiary.rkt" to contain your bestiary or "scenario_123_foes.rkt" for the foes specification for a scenario. We’ll cover the exact formats of various scenario programs in the next sections, starting with Bestiary Format by Example.

5.2 Bestiary Format by Example🔗

Let’s start with an example bestiary. This is the one that comes with Frosthaven Manager to try things out:

 1   #lang frosthaven-manager/bestiary
 3   begin-monster
 4       "hynox archer"
 6       [0 normal [hp 2] [move 2] [attack 2]]
 7       [1 normal [hp 3] [move 3] [attack 3]]
 8       [2 normal [hp 4] [move 4] [attack 4]]
 9       [3 normal [hp 5] [move 5] [attack 5]]
10       [4 normal [hp 6] [move 6] [attack 6]]
11       [5 normal [hp 7] [move 7] [attack 7]]
12       [6 normal [hp 8] [move 8] [attack 8]]
13       [7 normal [hp 9] [move 9] [attack 9]]
15       [0 elite [HP 4]  [Move 2] [Attack 3]  [Bonuses {"shield 1"}]]
16       [1 elite [HP 5]  [Move 3] [Attack 4]  [Bonuses {"shield 1"}]]
17       [2 elite [HP 6]  [Move 4] [Attack 5]  [Bonuses {"shield 1"}]]
18       [3 elite [HP 7]  [Move 5] [Attack 6]  [Bonuses {"shield 2"}]]
19       [4 elite [HP 8]  [Move 6] [Attack 7]  [Bonuses {"shield 2"}]]
20       [5 elite [HP 9]  [Move 7] [Attack 8]  [Bonuses {"shield 2"}]]
21       [6 elite [HP 10] [Move 8] [Attack 9]  [Bonuses {"shield 3"}]]
22       [7 elite [HP 11] [Move 9] [Attack 10] [Bonuses {"shield 3"}]]
23   end-monster
25   begin-ability-deck
26       "archer"
28       ["double-shot" 25 {"attack +2, range 5" "attack +2, range 3, +1 if same target"}]
29       ["double-shot" 25 {"attack +2, range 5" "attack +2, range 3, +1 if same target"}]
30       ["double-shot" 25 {"attack +2, range 5" "attack +2, range 3, +1 if same target"}]
31       ["double-shot" 25 {"attack +2, range 5" "attack +2, range 3, +1 if same target"}]
32       ["double-shot" 25 {"attack +2, range 5" "attack +2, range 3, +1 if same target"}]
33       ["double-shot" 25 {"attack +2, range 5" "attack +2, range 3, +1 if same target"}]
34       ["double-shot" 25 {"attack +2, range 5" "attack +2, range 3, +1 if same target"}]
35       ["take aim" 80 shuffle {"move +2" "strengthen self"}]
36   end-ability-deck
38   begin-monster
39       "wyrmling archer" ("archer")
41       [0 normal [hp 1] [move 1] [attack 1]]
42       [1 normal [hp 2] [move 2] [attack 2]]
43       [2 normal [hp 3] [move 3] [attack 3]]
44       [3 normal [hp 4] [move 4] [attack 4]]
45       [4 normal [hp 5] [move 5] [attack 5]]
46       [5 normal [hp 6] [move 6] [attack 6]]
47       [6 normal [hp 7] [move 7] [attack 7]]
48       [7 normal [hp 8] [move 8] [attack 8]]
50       [0 elite [hp 3] [move 1] [attack 2] [Bonuses {"shield 1"}]]
51       [1 elite [hp 4] [move 2] [attack 3] [Bonuses {"shield 1"}]]
52       [2 elite [hp 5] [move 3] [attack 4] [Bonuses {"shield 1"}]]
53       [3 elite [hp 6] [move 4] [attack 5] [Bonuses {"shield 2"}]]
54       [4 elite [hp 7] [move 5] [attack 6] [Bonuses {"shield 2"}]]
55       [5 elite [hp 8] [move 6] [attack 7] [Bonuses {"shield 2"}]]
56       [6 elite [hp 9] [move 7] [attack 8] [Bonuses {"shield 3"}]]
57       [7 elite [hp 10] [move 8] [attack 9] [Bonuses {"shield 3"}]]
58   end-monster
60   begin-monster
61       "hynox guard"
63       [0 normal [hp 2] [move 2] [attack 2]]
64       [1 normal [hp 3] [move 3] [attack 3]]
65       [2 normal [hp 4] [move 4] [attack 4]]
66       [3 normal [hp 5] [move 5] [attack 5]]
67       [4 normal [hp 6] [move 6] [attack 6]]
68       [5 normal [hp 7] [move 7] [attack 7]]
69       [6 normal [hp 8] [move 8] [attack 8]]
70       [7 normal [hp 9] [move 9] [attack 9]]
72       [0 elite [hp 4] [move 2] [attack 3] [Bonuses {"shield 1"}]]
73       [1 elite [hp 5] [move 3] [attack 4] [Bonuses {"shield 1"}]]
74       [2 elite [hp 6] [move 4] [attack 5] [Bonuses {"shield 1"}]]
75       [3 elite [hp 7] [move 5] [attack 6] [Bonuses {"shield 2"}]]
76       [4 elite [hp 8] [move 6] [attack 7] [Bonuses {"shield 2"}]]
77       [5 elite [hp 9] [move 7] [attack 8] [Bonuses {"shield 2"}]]
78       [6 elite [hp 10] [move 8] [attack 9] [Bonuses {"shield 3"}]]
79       [7 elite [hp 11] [move 9] [attack 10] [Bonuses {"shield 3"}]]
80   end-monster
82   begin-ability-deck
83       "guard"
85       ["rushing charge" 25 {"move +3" "attack +2 + number of spaces moved towards target"}]
86       ["rushing charge" 25 {"move +3" "attack +2 + number of spaces moved towards target"}]
87       ["rushing charge" 25 {"move +3" "attack +2 + number of spaces moved towards target"}]
88       ["rushing charge" 25 {"move +3" "attack +2 + number of spaces moved towards target"}]
89       ["rushing charge" 25 {"move +3" "attack +2 + number of spaces moved towards target"}]
90       ["rushing charge" 25 {"move +3" "attack +2 + number of spaces moved towards target"}]
91       ["rushing charge" 25 {"move +3" "attack +2 + number of spaces moved towards target"}]
92       ["stand tall" 80 shuffle {"shield 3"}]
93   end-ability-deck

This is a bit long, but we can already identify several distinct components:
  1. Line 1 marks the file as containing a bestiary.

  2. Lines 3, 38, and 60 begin monsters.

  3. Monsters have names and many stats, like on lines 4–22.

  4. Lines 25 and 82 begin ability decks.

  5. Ability decks have ability cards, lik on lines 28–35.

There is a lot of whitespace to help provide a visual feel of the layout. You are encouraged to use whitespace for layout, but the Frosthaven Manager will ignore it in most cases.

You might notice double-quotes " around names like "guard" or "hynox archer". These denote game text, which is always enclosed by double-quotes to group the text together.

You might notice some numbers, like on line 5, are not in quotes. These are game numbers, like max HP. For Frosthaven Manager you should only see positive natural numbers, like 1, 2, 3, 42, etc. Contrast these with numbers written in quotes, like in "shield 3" on line 21: these numbers are part of the game text, like in an ability.

With these observations, we’re ready to write a monster definition!

5.2.1 Write Your Own Monster🔗

We’ll start with the monster information, which contains all the statistics needed for different levels of the monster. To begin, we write

Don’t forget to put #lang frosthaven-manager/bestiary at the top of the file!


This indicates a monster definition, as observed. Every part of the information goes between this opener and the closer end-monster.

The next two things are the Monster name and Set name. For example, a Hobgoblin Warrior would have a Monster name of "Hobgoblin Warrior" and a Set name of "Warrior." Both of these are game text, so don’t forget the quotes. The Set name belongs in parentheses because in many cases it can be omitted, in which case the last word of the Monster name is used. So we can write either

"Hobgoblin Warrior"


"Hobgoblin Warrior" ("Warrior")

Next comes the normal and elite monster stats. Each stat block is sandwhiched in square brackets like [ …stat block… ]. Inside, a stat block always starts with a level, which is a game number between 0 and 7, and a type, which is either normal or elite. Then we need to add the actual stats. Once you’ve seen one example of monster stats, you’ve seen most of them!

The three mandatory elements of the monster stats are max HP, base movement, and base attack. These are all game numbers. Let’s say the Hobgoblin Warrior has 4 max HP, move 2, and attack 2 at level 0:

[0 normal [HP 2] [Move 2] [Attack 2]]

We can rearrange the HP and other stats, as long as they come after the level and the type and are within the square brackets.

The next three elements of the monster stats are optional. They are persistent bonuses, attack effects, and condition immunities. All three are game text, which means quotes. Bonuses are things like "Shield 1." Attack effects are things like "2 Targets" and "Pierce 1." Condition immunities are things like "Poison." For many monsters, all three groups will be empty, so you can leave them out. Let’s use the examples in this paragraph for the Hobgoblin Warrior, even though it might be far too strong for level 0, just to show how to write them:

[0 normal [HP 2] [Move 2] [Attack 2]
          [Bonuses {"Shield 1"}]
          [Effects {"2 Targets" "Pierce 1"}]
          [Immunities {"Poison"}]]

Notice that we can put multiple, whitespace-separated chunks of game text in curly brackets, like  {"one" "two"} . Since there can be many bonuses, effects, or immunities, we collect them in a list like this.

Now you’ve seen it all: we need to write 16 total monster stats for the Hobgoblin Warrior, which we’ll do by making up the numbers as we go. Then we finish out with end-monster. Normally we would probably refer to an existing Frosthaven monster stats card, but you can also make your own.

    "Hobgoblin Warrior"
    [0 normal [hp 4] [move 2] [attack 2]]
    [1 normal [hp 5] [move 2] [attack 2]]
    [2 normal [hp 6] [move 3] [attack 3]]
    [3 normal [hp 7] [move 3] [attack 3]]
    [4 normal [hp 8] [move 3] [attack 4]]
    [5 normal [hp 9] [move 3] [attack 4]]
    [6 normal [hp 10] [move 4] [attack 5]]
    [7 normal [hp 11] [move 4] [attack 5]]
    [0 elite [hp 6] [move 3] [attack 3] [bonuses {"Shield 1"}] [effects {"2 Targets" "Pierce 1"}] [immunities {"Poison"}]]
    [1 elite [hp 7] [move 3] [attack 3] [bonuses {"Shield 1"}] [effects {"2 Targets" "Pierce 1"}] [immunities {"Poison"}]]
    [2 elite [hp 8] [move 4] [attack 4] [bonuses {"Shield 2"}] [effects {"2 Targets" "Pierce 1"}] [immunities {"Poison"}]]
    [3 elite [hp 9] [move 4] [attack 4] [bonuses {"Shield 2"}] [effects {"2 Targets" "Pierce 1"}] [immunities {"Poison"}]]
    [4 elite [hp 10] [move 4] [attack 5] [bonuses {"Shield 2"}] [effects {"2 Targets" "Pierce 1"}] [immunities {"Poison"}]]
    [5 elite [hp 11] [move 4] [attack 5] [bonuses {"Shield 2"}] [effects {"2 Targets" "Pierce 1"}] [immunities {"Poison"}]]
    [6 elite [hp 12] [move 5] [attack 6] [bonuses {"Shield 3"}] [effects {"2 Targets" "Pierce 1"}] [immunities {"Poison"}]]
    [7 elite [hp 13] [move 5] [attack 6] [bonuses {"Shield 3"}] [effects {"2 Targets" "Pierce 1"}] [immunities {"Poison"}]]

That’s an entire monster definition for Hobgoblin Warriors. But right now it doesn’t have any ability cards!

Let’s write an ability deck. Like before, we start at the beginning:


Next we have the Set name to which the ability card belongs. Recall that, for example, archers all use the same deck. Here we want to use the "Warrior" set.


Now we need some cards. Like stats, these go in square brackets in a particular format. They start with name and initiative.

Let’s write the "Crushing Blow" card. I think this is a slow ability, so let’s go with 65:

["Crushing Blow" 65]

All ability cards have a list of abilities, which are game text. I think Crushing Blow should have a short move and a big attack, so lets add two abilities for that:

["Crushing Blow" 65 {"Move 2" "Attack +4, Muddle"}]

The last thing we need is to indicate whether this is a "shuffle" card or not—recall that some ability cards trigger a re-shuffle of the ability deck for that set. As usual, if the deck ever runs out, it is also re-shuffled.

Crushing Blow doesn’t seem like the kind of ability that needs to shuffle the ability deck, so we don’t need to add anything else to that card. But an ability like Rallying Cry could trigger a shuffle:

["Rallying Cry" 10 shuffle {"Shield 1" "Heal 2, range 2"}]

Since we wanted this card to trigger a shuffle, we wrote shuffle between the initiative and the list of abilities.

To finish off the deck, we need 6 more cards! We’ll gloss over those here. Here’s what it would look like:

    ["Crushing Blow" 65 {"Move 2" "Attack +4, Muddle"}]
    ["Rallying Cry" 10 shuffle {"Shield 1" "Heal 2, range 2"}]

And that’s how you write a monster or ability deck!

5.2.2 Using Multiple Bestiaries🔗

It’s possible to import the monsters from one bestiary into another. The command import-monsters does this when given a filename written as game text. For example:

import-monsters "guards.rkt"

would import all the monsters and ability decks in the file "guards.rkt" for use in the current bestiary.

Use the "/" slash character to separate folders and directories from filenames. If there is no slash, the bestiary from which to import is assumed to be in the same folder or directory as the bestiary containing the import command. Use ".." to mean one level above the folder or directory containing the bestiary with the import command, so that the following command imports "monsters.rkt" from one directory above the current bestiary’s containing folder or directory:

import-monsters "../monsters.rkt"

One organizational strategy for your bestiaries is the "One Set per File" rule. Each monster set gets its own bestiary file with an ability deck and monsters. For example, all the "guard" monsters go with the guard ability deck in "guards.rkt".

Then you might have a final bestiary "my_bestiary.rkt" that imports each set, like so:


#lang frosthaven-manager/bestiary
import-monsters "guards.rkt"

An example of this format can be found in the source.

You’ve now seen everything you need to write your own bestiary. Below, you’ll find a succint reference for the format.

5.3 Bestiary Format Reference🔗

 #lang frosthaven-manager/bestiary
  package: frosthaven-manager

The Developer Reference for monster information documents the underlying data structures, like monster-info, monster-stats, and monster-ability.

The following terms are used both in the explanation by example:

The grammar for the bestiary is as follows. Whitespace is ignored except in game text.

Any bestiary file that defines a monster is required to define an ability deck for that monster’s set. Importing bestiaries that conflict with each other or with the current bestiary is also an error. Cyclic imports are disallowed.

Any monster’s attack or HP values can be specified as formulas, using the variables L and C to refer to the current level and number of characters, respectively. Formulas are written in game text, like "C + 2" or "L * 3". The available operators in formulas are the standard arithmetic operators "+", "-", "*", and "/". Use parentheses to group expressions, like "(C + 1) * 2". The functions up and down round up or down and are written "up(3/2)" or "down(L/3)".

Any monster’s move value can be specified as "no move value" by writing -, like [Move -].

Any card’s abilities in the text-list can include a reference to an AoE pattern by using the function aoe, like in the following example: "attack +1, aoe(path/to/triangle.rkt)". See Area-of-Effect Specification by Example for more details.

Any card can refer to elements by writing "Infuse" or "Consume" followed by the name of the element. This is case-insensitive, so "infuse" works, too. The element names are "fire", "ice", "air", "earth", "light", and "dark" or "darkness". Additional elements may follow, separated by commas, as in "Infuse Dark, Ice". You can also write "Infuse any" or "Infuse any element" for wild infusions and "Consume any" or "Consume any element" for wild consumptions. This is also case-insensitive. In all cases this text will be transformed to a corresponding pictorial representation.




{import  |  monster  |  ability deck}*




import-monsters file:text





name:text [( set:text )]






[ level:number {normal  |  elite} stat-block ]




hp move attack [bonuses] [effects] [immunities]




[ HP {number  |  formula:text} ]




[ Move {number  |  -} ]




[ Attack {number  |  formula:text} ]




[ Bonuses text-list ]




[ Effects text-list ]




[ Immunities text-list ]


ability deck









[ name:text initiative:number [shuffle] text-list ]




{ text* }

Text like nonterminals should be replaced with their right-hand-sides. Text like literal should be typed exactly. Text like [optional] is optional. Text like repeat* can be repeated 0 or more times, while repeat{n,n} should be repeated exactly n times. Text like {one  |  two} is a choice between each part, separated by bars. Text like label:text refers to game text, while label:number refers to an game number.

5.4 Area-of-Effect Specification by Example🔗

 #lang frosthaven-manager/aoe package: frosthaven-manager

When specifying a monster’s abilities, sometimes you need to designate an Area-of-Effect (AoE, for short). Here we’ll describe how to concisely describe the AoE pattern for use with the monster ability. See Bestiary Format Reference for how to refer to an AoE pattern in a monster ability.

As usual, we’ll put the description in a file and start with the language, in this case #lang frosthaven-manager/aoe. Then we describe the hexagonal layout using the symbols s, x, o, m, and g. You must put a space between one symbol and the next, as some examples will show. These symbols correspond to the following hexagons in the resulting diagram:


The symbol g is an invisible "ghost" hex, and is useful when you need to manually force empty space between hexes. In most cases, however, this should be taken care of automatically. For example, the following two programs create the same diagram, shown below:

#lang frosthaven-manager/aoe
x g x


#lang frosthaven-manager/aoe
x   x

both render as follows:

"x x"


"x g x"


Here are some more examples, along with their results.


#lang frosthaven-manager/aoe
 x x
x x x
 x x


#lang frosthaven-manager/aoe
x x


#lang frosthaven-manager/aoe
x x x
 o m


#lang frosthaven-manager/aoe

5.5 Area-of-Effect Specification Reference🔗

Space at the beginning of lines is significant, but not at the end. The line with the left-most character determines which rows are centered and which rows are offset: alternating rows are always offset relative to each other to create a hexagonal grid. It is not technically necessary to offset each line in the textual diagram to achieve correct results, but it is far more readable if you do so. Thus, we recommend starting alternating lines with either no spaces or 1 space, to create a hex-like effect in the program text.

Recall that all hexagons face North: that is, the top of the hexagon is a point and not a flat line.

Space must separate hex descriptors to distinguish them.

The following descriptors are available


In addition, the descriptor g places an invisible ghost hex; this is usually not necessary, since spacing takes care of the alignment automatically.

If you have Racket installed, running the programs main module as in
  racket my-aoe.rkt
will produce the image.

5.6 Foe Specification by Example🔗

Here’s an example foe specification:

 1 #lang frosthaven-manager/foes
 3 import-monsters "sample-bestiary.rkt"
 5 begin-foe
 6   "wyrmling archer"
 7   <[2 absent] [3 normal] [4 elite]>
 8   <[2 normal] [3 elite] [4 elite]>
 9 end-foe
11 begin-foe
12   "hynox guard" ("guard") (random numbering)
13   <[2 elite] [3 elite] [4 elite]>
14 end-foe

This foe specification imports a sample bestiary and defines two groups of foes using syntax similar to that of #lang frosthaven-manager/bestiary. One is for wyrmling archers and the other for hynox guards. For each group, we list whether a monster should be absent, normal, or elite for each of 2, 3, or 4 players. We can optionally specify how to number the monsters and what set the monster is in, if it cannot be inferred from the name.

In this example, a 2-player game faces 1 normal wyrmling archer (numbered 1) and one randomly-numbered elite hynox guard; a 3-player game has one normal and one elite wyrmling archers (numbered 1 and 2, respectively) and one randomly-numbered elite hynox guard.

5.7 Foe Specification Format Reference🔗

 #lang frosthaven-manager/foes package: frosthaven-manager

The Developer Reference for monster information documents the underlying data structures, like monster-group.

The grammar for the specification is as follows. Whitespace is ignored except in game text.

Any foe specification file that defines a monster is required to define an ability deck for that monster’s set. Importing bestiaries that conflict with each other or with the current file is also an error. Cyclic imports are disallowed.

The default numbering option is ordered.




{import  |  monster  |  ability deck  |  foe}*





name:text [( set:text )]

[( how-to-number numbering )]






ordered  |  random




< [ 2 foe-type ] [ 3 foe-type ] [ 4 foe-type ] >




absent  |  normal  |  elite

See Bestiary Format Reference for information on import, monster, and ability deck, as well as how to read this grammar.

5.8 Programming Loot🔗

 #lang frosthaven-manager/loot-cards
  package: frosthaven-manager

Like other languages described here, all loot programs start with #lang frosthaven-manager/loot-cards in the first line and typically have a ".rkt" suffix.

The following commands may be used in frosthaven-manager/loot-cards. The documentation for each command shows how to use it.



This command, written by itself, declares that the following program is based on the standard set of Frosthaven loot cards. It is mandatory for all loot programs.

This command may be repeated; each use discards the effects of all prior commands.


(sticker [stickers card] ...)

card = (money amount)
  | (material 2p 3p 4p)
  | herb
  | (herb amount)
  stickers : number?
  amount : number?
  2p : number?
  3p : number?
  4p : number?
This command is written parenthesized as shown. It declares that the named cards should have a number of + 1 stickers added equal to stickers. The card must be in the deck.

To specify a card, you write (money amount) for the money card worth a certain amount. You write (material 2p 3p 4p), where material is any of lumber, hide, or metal, for the material card that gives a number of resources for 2 players (2p), 3 players (3p), and 4 players (4p). You write arrowvine, axenut, corpsecap, flamefruit, rockroot, or snowthistle for the herb card worth 1 herb. You can also parenthesize and provide an amount for herb cards worth more.

This command may be repeated.


(add-special-loot name ...)

  name : string?
This command is written parenthesized as shown. It declares special loot cards for each name. You can use this for custom loot cards or for standard cards with special rules.

These cards are always included in the loot deck if available.

This command may be repeated.

Here is an example loot program that adds 1 sticker to a 2/2/1 lumber card:


#lang frosthaven-manager/loot-cards
(sticker [1 (lumber 2 2 1)])