Enemies
Components
Enemy Envelope
The enemy envelope is the bag that contains all components for a new enemy of that type. It will not be shown to the players, so a regular bag is sufficient, no extra model needed.
The bag must contain the Enemy Bag, the Stats-Sheet and the Ability Deck in any order.
The bag itself will never be placed on the table by the mod, so it shouldn’t contain and scripts as those would never be executed. It’s sole function is to group all objects belonging to a single enemy type.
Enemy Bag
This is an infinite bag containing the Figure for the enemy. It will be placed on the monster mat and new enemies will be spawned from the bag.
Lua Script
The Enemy Bag is currently the best place to register the enemy. This is typically done in two steps.
First the enemy’s ability deck has to be registered.
This is done by calling EnemyApi.registerEnemyAbilityDeck
.
It takes the name of the ability deck and the list of abilities it includes.
The abilities can have two attributes:
-
An
image
with the link to the image for this ability card. This value isn’t used for 1.3 yet, so it can also be ignored. It will be used for future versions, e.g. to the show the active ability card of an enemy in the initiative tracker. -
A
spawn
element, describing which object the ability can spawn (like traps or summoning other enemies). Documentation about the proper attributes for this element can be found here
This part should be skipped, if the enemy uses an ability deck that is already registered in the game. This is the case for all decks from the Gloomhaven base game.
local EnemyApi = require("api.EnemyApi")
local R = require("api.Resource")
EnemyApi.registerEnemyAbilityDeck("Archer", {
abilities = {
[1] = {
image = "https://raw.githubusercontent.com/any2cards/gloomhaven/master/images/monster-ability-cards/archer/ma-ar-1.png",
},
-- abilities 2 till 6 would be here too
[7] = {
image = "https://raw.githubusercontent.com/any2cards/gloomhaven/master/images/monster-ability-cards/archer/ma-ar-7.png",
spawn = {
{
element = {
type = R.ElementType.Trap,
name = "Spike Trap",
damage = 3,
},
}
},
},
[8] = {
image = "https://raw.githubusercontent.com/any2cards/gloomhaven/master/images/monster-ability-cards/archer/ma-ar-8.png",
},
}
}
Once the ability deck is registered, the actual enemy type has to be registered, to connect the enemy to the ability deck.
This is done by calling EnemyApi.registerEnemy
.
This function takes the name of the enemy type and two named parameters:
-
icon
is the link to an image for an icon of this enemy type. This is currently used in the Context Menu, when the enemy can be summoned by other enemies. -
abilityDeck
is the name of a previously registered ability deck. This tells the mod that this enemy uses the ability deck with this name.
local EnemyApi = require("api.EnemyApi")
EnemyApi.registerEnemy("Bandit Archer", {
icon = "http://cloud-3.steamusercontent.com/ugc/1688272925075790807/F181B084F5CA91BC0E0F75C682C6993C4BCBDA43/",
abilityDeck = "Archer",
})
If the enemy is a boss enemy, another API has to be used instead: EnemyApi.registerBossEnemy
.
Since all bosses use the same ability deck, this is where their abilities are defined (e.g. which elements they can spawn).
The spawn
element works the same as for registerEnemyAbilityDeck
and details can be found here.
local EnemyApi = require("api.EnemyApi")
local R = require("api.Resource")
EnemyApi.registerBossEnemy("Bandit Commander", {
icon = "http://cloud-3.steamusercontent.com/ugc/1688272925075790870/264D1A7A6C93A37894B204DD6CDB555F11B8B5BD/",
spawn = {
{
element = {
type = R.ElementType.Enemy,
name = "Living Bones",
},
},
},
})
Figure
Name |
Full name of the enemy type. |
Description |
Must be empty. |
Tags |
|
Lua Script
Put the following Lua script on the Figure.
The value for FrameOffset
defines where the HP will be placed at.
Adjust the value to fit the model.
You can use this tool to easily try out different values.
FrameOffset = 260
require("Figures.Monster")
Stats-Sheet
Properties
Name |
Full name of the enemy followed by " Stat Sheet", e.g. "Bandit Archer Stat Sheet". |
Tags |
|
Lua Script
Within the script of the Stats-Sheet the stats for the different levels are defined as well as how many figures can be present for this enemy type.
To define the maximum number of enemies, declare a variable named count
.
Put the following script on the Stats-Sheet and adjust the values accordingly.
count = 4
require("StatSheet")
The stats per level are declared using the variable named stats
.
This variable is a table of stat entries, one per level.
You can use a numeric index, to more easily set the values per level.
stats
variablestats = {
[0] = {
-- Level 0 stats go here
[1] = {
-- Level 1 stats go here
}
-- etc.
}
require("StatSheet")
The entry for each level has an attribute named orientation
and the attributes normal
and elite
.
In case of a boss enemy the level has an attribute boss
instead of normal
and elite
.
The orientation
attribute defines the rotation of the Stat-Sheets object that shows that stats for this level.
This is used by the mod to figure out which level is currently set on the Stats-Sheet by checking the current orientation of the object with the entries defined here.
stats = {
[0] = {
orientation = {0, 180, 0},
normal = {
-- stats for normal version go here
},
elite = {
-- stats for elite version go here
},
boss = {
-- this is used by bosses instead of the above two
}
},
}
require("StatSheet")
Within normal
, elite
or boss
the actual stats have to be defined.
Checkout Stats for the available attributes.
-- maximum number of standees for this enemy type
count = 4;
-- stats per level
stats = {
[0] = {
-- this is the orientation the stat that shows the level
orientation = {0, 180, 0},
-- stats for the normal version
normal = {
health = 8,
move = 3,
attack = 2,
range = 0,
attributes = {}
},
-- stats for the elite version
elite = {
health = 13,
move = 3,
attack = 3,
range = 0,
attributes = {}
}
},
-- same for level 1
[1] = {
orientation = {0, 90, 0},
normal = {
health = 9,
move = 3,
attack = 2,
range = 0,
attributes = {
"Poison"
}
},
elite = {
health = 15,
move = 3,
attack = 3,
range = 0,
attributes = {
"Wound"
}
}
},
-- level 2 and 3 omitted
[4] = {
-- level 4 is the same, but now the back of the stat sheet is used,
-- so the rotation on the z-axis has to be changed to account for that
orientation = {0, 180, 180},
normal = {
health = 16,
move = 3,
attack = 4,
range = 0,
attributes = {
"Poison"
}
},
elite = {
health = 24,
move = 4,
attack = 4,
range = 0,
attributes = {
"Poison",
"Wound"
}
}
},
-- and so on until level 7
}
require("StatSheet");
Ability Deck
The Ability Deck contains the different Ability Cards.
Ability Card
Each Ability Card within the Ability Deck needs to have a special naming structure and the Monster Ability Card
tag.
Name |
Name of the Ability Deck (e.g. "Archer"). |
Description |
Definition of the initiative (see below). |
The description field is used to encode the initiative values(s) and the action(s) the enemy takes.
Each entry has to start with an initiative value followed by a colon and a space. The initiative value needs to have a leading zero if it’s below 10.
After the colon, the textual description can be added. This is used in the initiative tracker to show what the enemy is doing. Multiple actions can be defined by using a semicolon. Those will show up as separate lines in the initiative tracker.
31: Move +0; Attack +0
For abilities that shuffle the ability cards, add shuffle
after the initiative.
15 shuffle: Move +1; Attack +1
If the ability grants multiple actions at different initiatives, you can use new lines. Each line will add an entry to the initiative tracker.
03: Attack +0 11: Move +0 20: Attack +0
To use icons in the initiative tracker, you must change your player color to black and update the GM Note instead of the Description field. You can use the following text for icons:
-
{e.Attack}
-
{e.Move}
-
{e.Heal}
-
{e.Loot}
-
{e.Shield}
-
{e.Retaliate}
-
{e.Range}
-
{e.Target}
-
{e.Damage}
-
{e.Jump}
-
{e.Fly}
-
{e.Teleport}
-
{e.Generate Fire}
-
{e.Generate Ice}
-
{e.Generate Air}
-
{e.Generate Earth}
-
{e.Generate Light}
-
{e.Generate Dark}
-
{e.Generate Any}
-
{e.Consume Fire}
-
{e.Consume Ice}
-
{e.Consume Air}
-
{e.Consume Earth}
-
{e.Consume Light}
-
{e.Consume Dark}
-
{e.Consume Any}
You can also include an icon for any custom registered effect or condition using {e.<Effect>}
or {c.<Condition>}
respectively.
03: {e.Attack}+0 11: {e.Move}+0 55: {c.Poison}
You can add other custom conditions/effects, as long as they are already part of the Markazi Gloom Color font by Dimon-II. To add them, the registerCondition and registerEffect functions allow a new entry called renderedMarkup which takes any string. Whatever you put there will be shown in the preview whenever the markup for this Condition/Effect is used. There are already helper via the new UiApi where you only need to provide the glyph and color used for the condition. E.g. for a condition, all you need to do is to find the correct glyph in the font and insert it into the first parameter of UiApi.conditionText \u{<Glyph>}
. The color for the condition as hex value is the second parameter.
local UiApi = require("api.UiApi")
ConditionApi.registerCondition("Safeguard", {
image = "...",
immunity = { ... },
renderedMarkup = UiApi.conditionText("\u{E09D}", "#5B9977"),
})
You can include an AoE "Area" which will display an AoE pattern in the initiative tracker. This can be designated using {area.<Rows>}
with specific syntax to create the intended pattern:
-
s
will create a grey "self" hex -
t
will create a red "enemy" hex -
a
will create a blue "ally" hex -
e
will create a blank space with no hex -
_
will begin a new row, every even row will be automatically intented by half a hex
-- first row: empty hex and two target hexes
-- second row: the self hex and a target hex
{area.ett_st}
-- second area
{area.ett_stt_ett}
Stats
Stats for enemies and summons have the following structure.
local stats = {
-- The maximum health values
health = 8,
-- The base move value
move = 3,
-- The base attack value
attack = 2,
-- The base range value
range = 0,
-- A list of extra effect like retaliate shield
attributes = { },
-- A list of immunities
immunities = { },
-- A text that will be shown below the healthbar
-- This can be used for describing what a variable means, e.g. on a boss
-- like, "V is the number of ..."
text = "",
}
Health
This attribute takes a single value. For Boss enemies this can also be a string in the form of "8xC", which will calculate the value based on player count. A Formula is currently not supported.
Move
The move
attribute can either be a simple value (a number or string like above), or a table with the attributes value
and type.
The attribute value
determines how for the figure can move and the type
determines the movement type (e.g. for flying enemies).
The value for this field can the name of any registered Effect, but generally should be "Fly", "Jump", or "Teleport".
-- A flying enemy
move = {
value = 3,
type = "Fly"
}
Attack
The attack
attribute functions similar.
It can take a simple value or a table with the attributes value
and area.
The attribute value
determines the attack value of the figure the area
attribute is the name of an Effect.
The image for this Effect will be shown next to the attack value.
Its purpose is to show the attack area for this figure, but it could be used for any other effect as well.
-- An enemy with a special attack area
attack = {
value = 3,
area = "Area X"
}
Immunities
The immunities
field is a list of Effects this figure is immune to.
immunities = { "Poison", "Pull", "Curse", "Stun" }
Attributes
The attributes
field describes extra attributes for the enemy, like if it has a shield value, or retaliated damage or always applies a certain condition.
It’s a list that can contain three kinds of different values:
-
A simple string. This is the name of any registered Effect, e.g. "Generate Air", "Muddle" or "Curse"
-
A name, value pair. The name is the name of any registered effect and the value is the value for this Effect, e.g. "Shield = 2" or "Retaliate = 2". This will show the image for the Effect followed by a text with this value.
-
A name with a table that has the attributes
value
andrange
. This is used for retaliated damage that has a range. It will show the image for the Retaliate Effect and it’s value, followed by the image for Range and it’s value. While this is currently only used for Retaliate, this format can be used for any other Effect as well.
attributes = { -- the name of an Effect "Generate Air", -- the name of an Effect and it's value Shield = 2, -- short for for Retaliate, if the range is 1 Retaliate = 2, -- special form for Retaliate, with a range greater than 1 Retaliate = { value = 2, range = 2 }, }