Card Hunter Utils  —  Overview  —  Originally by neoncat  —  Maintained by PhoenixTheHunter  —  CH Forum Thread

Cheat Sheet for Custom Filter

This document should guide you through the process of defining your own custom filter for tools like the collection analysis tool or the card and item query tool. The custom filter is similar to what the Keep's filter does but allows a more fine-grained filtering. Unfortunately, this comes with a more complex syntax and requires a basic knowledge of the equipment and card description files provided by the game server.

Table of Contents

The Equipment and Card Description Files [top]

The game server provides - among other files, of course - two files describing the available items and the existing cards. These files contain data in a table-like format with column-headers. Both files are included in the tool's source code for further clarification or inspiration. To define a custom filter you need these column-headers, therefore, the first step is to get to know them.
ATTENTION: The capitalization is important for the filter. If you misspell the headers the filter won't find the expected values.

Equipment Description File

Here are the column-headers of the equipment description file. The corresponding values are strictly non-empty unless stated otherwise. Emphasized (italic) headers are very likely to be irrelevant for most users.
HeaderFormatpossibly empty?Description
IdNumbera unique number identifying the item
Equipment NameTextthe item's name as used in-game
Short NameTexta shorter name for certain cases where only limited space is available - only set if the original name is too long
RarityText"Common", "Uncommon", "Rare", "Epic" or "Legendary"
LevelNumberthe item's level
Introductory LevelNumberthe renown you have to have to find the item in campaign loot - empty for treasures
Talent 1Numberthe first power token: "0" represents no token requirement, "1" for a minor token, "2" for a major token - empty for treasures
Talent 2Numberthe second power token, "-1" if not weapon, divine weapon or staff, otherwise see encoding above - empty for treasures
Card 1 (up to Card 10)Textthe name of the corresponding card - empty if item has less cards or is treasure
SlotText"Arcane Item", "Arcane Skill", "Boots", "Divine Armor", "Divine Item", "Divine Skill", "Divine Weapon", "Dwarf Skill", "Elf Skill", "Heavy Armor", "Helmet", "Human Skill", "Martial Skill", "Robes", "Shield", "Staff", "Treasure", "Weapon"
SetNumber"0" for base items, "1" for AotA, "2" for EttSC, "3" for AA, "4" for AI, "5" for CM
Total ValueNumberthe calculated item level based on the contained cards, usually "5" for treasures
Slot DefaultTextcontains the slot name to identify this item as default for its slot (usually filtered out before the custom filter gets applied) - empty for any actual collectible item
ImageTextthe name of the image file that displays this item
TagsTextspecial hints for the game (like "noLoot" for items that cannot drop) - empty for any regular item
Manual RarityTexta manual set rarity for items that wouldn't meet the developer's demands otherwise
Manual ValueNumbera manual set value (and therefore level) for items that wouldn't meet the developer's demands otherwise

Card Description File

Here are the column-headers of the card description file. Columns that contain a "(2)" are available either with or without an additional "2", hence, there is a header "Trigger" and a header "Trigger 2". The corresponding values can be empty unless stated otherwise. A check mark in parentheses signalize columns that are non-empty in most cases but technically can be empty. Emphasized (italic) headers are very likely to be irrelevant for most users.
HeaderFormatstrictly non-empty?Description
IDNumbera unique number identifying the card
Card NameTextthe card's name as used in-game
Short NameTexta shorter name for certain cases where only limited space is available - only set if the original name is too long
TypesText"Armor", "Assist", "Attack", "Block", "Boost", "Handicap", "Move", "Utility", or two of the types separated by comma
Attack TypeText"Melee", "Projectile", "Magic"
Damage TypeText"Acid", "Arcane", "Cold", "Crushing", "Electrical", "Fire", "Holy", "Laser", "Piercing", "Poison", "Psychic", "Radiation", "Silver", "Slashing", "Sonic", "Unholy"
DamageNumberthe number of damage points this card causes
Minimum RangeNumbera minimum number of cells between your character and the target
RangeNumberthe maximum number of cells between your character and the target
Move PointsNumberpossible move range for moves and step attacks
DurationNumberthe time a card stays attached
Trigger (2)Numberthe number your die roll has to match or exceed
Keep (2)Numberflag indicating if this card is discarded after activation
Trigger Effect (2)Textwhat type of effect does the roll have: "Armor", "Block", "Boost", "Handicap", "Move", "Special"
TextTextprimary (active) card effect text
Trigger Text (2)Textsecondary (passive) card effect text
Plus MinusText"+" or "-" if card is rated above or below other cards with the same quality
QualityTextthe card's quality (title color), "E" for Black, "D" for Paper, "C" for Bronze, "B" for Silver, "A" for Gold, "AA" for Emerald, "AAA" for Amethyst
Quality x (where x is either a class or a race)Textthe card's quality if part of items of this class or race
RarityText(✔)"Common", "Uncommon" or "Rare"
SetNumber"0" for base items, "1" for AotA, "2" for EttSC, "3" for AA, "4" for AI, "5" for CM
LevelNumberthe card's complexity, the item's introductory level is the highest level of the contained cards
SlotsTextthe slots this card may appear in, separated by comma (may greatly differ from the actual items containing this card)
Flavor TextTextmostly funny notes (anecdotes, (fictional) citations) without effect on the game
Play TextTexttext to show when card is (actively) played
Trigger Attempt Text (2)Texttext to show before die roll
Trigger Succeed Text (2)Texttext to show if roll succeeded
Trigger Fail Text (2)Texttext to show if roll failed
Component1 (up to Component5)Texttechnical game component this card is built upon
Params1 (up to Params5)Textthe parameter for the matching component
ParamsTextAI parameter
Function TagsTextAI hints
Attachment ImageTextimage to attach to terrain upon card effect
StatusTextflag to indicate the card's implementation status
Audio Key (2)Textsound file to play upon card effect
ArtTextflag to indicate the card's graphical status

The Syntax [top]

Given you know the headers that are relevant for you and the values you want to test against, the next step is to formulate your desired filter in terms of rules. There are two steps when creating a filter, creating the rules that actually check the property of some card or item and combining the simple check rules to much more powerful and complex filters. For advanced purposes there are also rules that combine those two aspects insofar as they check some property but also serve as a method of compostion. But the first two steps should be sufficient to construct most filters, so starters should focus on those easier methods of filter creation. All example rules and filters are clickable and will be opened as query in the card and item query tool with the correct search base already selected.

Checking Rules

Let's start the actual filter creation with writing checking rules. Checking rules are a direct translation of the relevant headers into the basic elements of each filter. There are two types of simple rules, one for text columns and one for number columns.

Text Rules

To test the value of a text column use the following pattern:
[column-header] comparator "value"
comparator: == (equals) or != (does not equal) or =~ (contains) or !~ (does not contain)
with square brackets "[", "]"

Examples:
[Slot] == "Boots" (only show items that are boots)
[Rarity] != "Common" (exclude common items/cards)
[Types] =~ "Move" (find cards that include some notion of movement)
[Quality] !~ "A" (find cards with quality at most silver)

Note that all headers can be queried with a text rule although you lose some of the filter power if you use text rules on number headers.
There is a special text rule style that tries to match against all columns (including number columns):
[] comparator "value"
Usually, this rule should be used with one of the "contains"-comparators.
Example:
[] =~ "Penetrating" (roughly find penetrating attack cards, checks whether any column of an item contains the given term)
[] !~ "Electrical" (exclude all cards that use the term Electrical somewhere, no column may contain the given term)

Number Rules

To test the value of a number column use the following pattern:
{column-header} comparator value
comparator: == or != or < or <= or > or >=
with curly brackets "{", "}"

Examples:
{Range} >= 3 (find long range cards)
{Talent 1} == 0 (find token-less items)
{Level} < 12 (find items of some level or below)

Applying number rules on textual header will always succeed but either exclude all or include all cards or items. Applying the number rule on columns that may be empty can yield false results because an empty entry isn't interpreted as a number. Consider making a textual empty-check first:
[Talent 1] != "" and {Talent 1} <= 1 (find all not-treasure items with at most minor token requirement, for other methods of rule composition see next segment)

Methods of Rule Composition

If you have your simple rules assembled, the next and final step is to compose your overall filter out of the simpler checking rules. For this composition step you can choose from the following composition methods:

Negation

Write an exclamation mark ("!") or the word "not" in front of a rule to negate it.
Example:
not [Types] == "Handicap" (exclude all pure handicap cards)
Note that most negated rules can be rewritten without explicit negation so it's just a matter of taste. The above filter is equivalent to:
[Types] != "Handicap"

Conjunction

Combine two or more rules with one (or two) "&"-sign(s) or the word "and" if all rules must match an item or card.
Example:
{Level} > 6 and {Level} <= 12 and [Rarity] =~"o" (find items that the Kyburz Market sells)

Disjunction

Combine two or more rules with one (or two) "|"-sign(s) or the word "or" if it is enough for one rule to match an item or card.
Example:
[Slot] =~ "Weapon" or [Slot] == "Staff" (find items with six cards)

Exclusive Disjunction

Combine exactly two rules with the "^"-sign or the word "xor" if either one of the rules has to match but not both at the same time.
Example:
[]=~"Penetrating" xor []=~"Unblockable" (find cards that are either penetrating or unblockable but not both penetrating and unblockable)

Grouping

Use parentheses to define groups.
Example:
[Slot] == "Weapon" and {Level} <= 6 and ([Rarity] == "Common" or [Rarity] == "Uncommon") (find easy to collect weapons)
This is especially important to take control over the evaluation order. A negation only applies to the next atomic rule. If you wish to negate the result of another rule combination you have to group the combination and write the not in front of the group:
not ([Rarity] == "Common" or [Rarity] == "Uncommon") (will exclude the most regular drops)
Furthermore do the types of rule combination have different binding strengths. They are ordered like: not > and > xor > or. Hence (given some simple checking rules a, b, c, d), the filter not a or b and c xor d is evaluated as (not a) or ((b and c) xor d). If you want or need another order of evaluation you have to add groups to your filter manually.

Checking Rule Compositions

This segment introduces some very advanced techniques that are necessary to build the most powerful filters. There are use cases where these techniques are really necessary, in others you could build your filter without them but you'll get a (much) longer filter. If you haven't executed any filters yet, I would advise you to skip this segment and try out the other parts first.

Items with Cards and Cards of Items

Until now it seemed as if testing an item or a card is done in the same style. But this isn't the whole truth. For the card and item query tool you can explicitly choose to find cards or items matching the given filter. The collection analysis tool will only apply the filter to items. Hence, all shown rules are valid syntax but only applicable in the correct context. But of course, there is also a way to apply card rules to all cards of an item or an item rule to all items that contain the given card. An item has three or six cards, therefore, you have to specify how many cards have to match in case you want to use a card rule on an item. Specifying this quantifier is done with the following syntax:
<quantifier> rule
quantifier: "!" (all cards match) or "?" (at least one card matches) or a number or a range separated with the "~"-sign (like 2~3)
rule: either a simple checking rule or a composed expression in parentheses

Examples:
<?>[Types] =~ "Block" (find items with a block card)
<!>({Range} > 5 or [Types] == "Assist") (find items with only assists or long range cards, but the items don't have to be strict assist items or long range items, every combination of assists and long ranged is fine)
<2>[Card Name] == "Chop" (find items with exactly two Chops (the 5-damage variant only, there may or may not be other chops, too on this item))
<3~6>[] =~ "Penetrating" (find items with at least three penetrating cards)

The same is possible to check all items with a certain card:
<!>{Level} > 18 (find cards that are only available on items with a level above 18 or no item at all)
Sometimes you don't want to apply a certain rule to all cards of an item or all items with a card but want to simply count them. In this case you can use the described rule syntax but more or less omit the actual rule. To signalize this you use "( )" as your rule.
<3>( ) (find items with three cards on them)
<?>( ) (find cards that are contained on some item(s))

Remembering rules

All previous mentioned checking rules already give you a large tool set for creating very powerful filters. But there is still one big problem, you can only test against pre-defined values. But what if you want to find items that only feature one card multiple times? Given that there are a lot of different cards you would have to create a very large filter rule that is combined of a check if card A is the only card on the item or if card B is the only card on the item or if card C ... . This is the point where the remembering rule comes into play. The remembering rule has the following syntax:
binding ~> rule
binding: @column-header#number or @@( with multiple of the simple bindings in here )
rule: either a simple checking rule or a composed expression in parentheses

In most other parts of the filter you may freely place spaces in order to create a more appealing or readable impression, but in places where you are naming column-headers you have to take care to not accidentally add spaces where there aren't any in the header. Whereas in the simple checking rules the brackets serve as header delimiter, the @...#-construct here is the header delimiter so you can't add additional spaces in between that aren't part of the column header. Similarly the #number-construct forms a unit you can't tear apart by spaces, hence, a simple binding can only be written this one way. If you make use of the multi-binding-variant ( @@(...) ) you can either write the simple bindings with or without spaces in between. If a value is bound to a number you can test against it using the notation #number.
Examples:
@Card 1#0 ~> ([Card 2] == #0 and [Card 3] == #0) (find items where the first three cards are identical regardless of the concrete card used)
@@( @Card 1#1 @Card 3#3) ~> ([Card 2] == #1 or [Card 2] == #3) (find items where the second card is either the same as the first or the third)
@Duration#0 ~> {Damage} < #0 (find cards that inflict less direct damage than the number of rounds they stay attached)

Note that the last filter will automatically filter out every card that either declares no Duration or no Damage. If your filter would handle cards with an empty column wrong, you again have to include separate empty checks. You can use any non-negative integer as the number to bind a value to. If you happen to use the same number twice the old bound value can be shadowed temporarily.

Compare with whole card/item pool

If you're working on an item or a card and you want to know if this one is unique or special in some way it is quite hard to compare it to others of the same type. Sure, you can compare a card to other cards by fetching the items with the first card and then check all the cards on those items (or the other way around if you start with items and want to compare them to other items). But this has several shortcomings: You will check several cards or items multiple times. You have to think about how to reach all cards or items that may be relevant. If your starting item or card is pretty unique you'll not find many associated cards or items. If you want to compare cards that aren't printed on items you're lost. Therefore, there is a syntax that omits the necessity of an <?><?>-operator and checks your card against every card and your item against every item. As this is somewhat similar to the "Items with Cards and Cards of Items" segment the operator and syntax are also alike:
>quantifier< rule
quantifier: "!" (all cards/items match) or "?" (at least one card/item matches) or a number or a range separated with the "~"-sign (like 2~3)
rule: either a simple checking rule or a composed expression in parentheses

As you can imagine this time omitting the rule will make no sense as the count of cards and items is static and has no benefit for your filter, so there always has to be a rule.
Examples:
@Card Name#0 ~> >2<[Card Name]=~#0 (find cards that have exactly one 'sibling' - an other card that has the same name except for some additional parts, like "Team Heal" and the enemy-only card "Super Team Heal")
@@(@Slot#0 @Image#1) ~> >?<([Slot]!=#0 and [Image]==#1) (find items that reuse images of another slot's item)

The Keep's Filter

Now that we have build our own first filters let's conclude the segment about the syntax with a comparison of the custom filters and the Keep's filter. The custom filter and the Keep's filter both have their advantages and disadvantages. The Keep focuses on easy to use filtering including some regular expression features. The custom filter allows explicit negation, conjunction, disjunction and last but not least specific field matching. Emulating a custom filter with the Keep's filter is impossible in general because the Keep's filter can't limit itself to some specific field(s) or allows to conjunct several checks. If you want to (roughly) emulate the Keep's filter with a custom filter you can use the following pattern but this will only work for simple word/text filtering (assuming you search for the term "Reliable" in the Keep):
[] =~ "Reliable" or <?>[] =~ "Reliable"

Concrete Scenarios [top]

In this section you'll find some concrete scenarios together with a custom filter that solves the described problem. There may be several solutions for each problem, so this will only be ONE possible solution.

Immunities

There are some nasty creatures with tricky armor. Especially immunities against piercing damage is tough because this prevents almost all penetrating warrior attacks. Don't want to equip armor removal or you don't have much of those cards? Work around the immunity:
<!>[Damage Type] != "Piercing" (all cards of an item may not use piercing damage type)
This will remove all items that use any number of piercing damage but cards like Spiked Mail will still show up (although the spiked effect won't work if the enemy has an immunity in hand).

Bad Luck

Some enemies use "Bad Luck", some players just have it regardless of the card. Using unreliable blocks won't yield good results in this situation. But if you only equip cards with a high success rate the blocks have to trigger eventually:
<!>(([Trigger] == "" or {Trigger} <=3) and ([Trigger 2] == "" or {Trigger 2} <=3))
This is another example for a textual empty-check together with a number condition. In case the entry is empty (because it's a pure attack card for example) the filter will accept it. If there is some data present it has to match the condition. As cards can have multiple effects needing a die rolls the filter has to check both trigger columns.

Handicap [and ...]

The developers think it may be tricky to complete the adventures with handicaps in hand. Prove them wrong. But how to find handicap cards?
<?>[Types] =~ "Handicap" (some cards of an item must be handicaps)
This will list items with handicap cards, both pure handicaps and hybrids. And it will only find cards that are of type handicap and not cards that involve handicaps (like Lateral Thinking). Or do you prefer hybrid handicaps?
<?>([Types] =~ "Handicap" and [Types] =~ ",") (some cards of an item must be hybrid handicaps)
If there is a comma in the types column you have a hybrid card, so conjunct both rules to find hybrid handicaps. Including further conditions isn't any problem now:
<?>[Types] =~ "Handicap" and <?>[Card Name] =~ "Bash" (items with both handicaps and bashes)
Combine this with the next scenario to maximize the number of bash cards.

Max Build

For some synergy effects it is important to equip as many cards of some type as possible. Class Skills are good examples for that. When facing many enemies chops are great because they more or less double the damage. But, how many chops are enough?
<3 ~ 6>[Card Name] =~ "Chop" (find items with three or more chop cards)
You can focus on strong chops for further boosts, but having three or more chop cards on each weapon yields a minimum of 9 chops in your deck. Facing blocks? Consider to exclude the clumsy ones from the selection:
<3 ~ 6>[Card Name] =~ "Chop" and <0>[Card Name] =~ "Clumsy" (to exclude them completely) or
<3 ~ 6>([Card Name] =~ "Chop" and [Card Name] !~ "Clumsy") (to allow them but exclude them from the calculated chop-card-count)

Power Token

You are running an adventure of level 12 or below? Then all your items with major power token requirement won't be equippable:
{Talent 1} <= 1 (check the first power token column for minor power token or none)
The important thing in this case is that the description file doesn't know what power tokens are but it declares talents (perhaps an old terminology?). This works well because the second power token (if any) is always of the same power level or below. With only a little adaption you can also find items with no power tokens.

Buying items from Shops

Most of you won't remember the time where you're renown wasn't at max. But back in the days it was quite expensive to buy powerful equipment. Use the following filter to check if you're missing items that you could buy for the regular price.
{Level} <= 14 (find items of level 14 or below)
If your renown is at 13 the above filter lists all items with regular price. You have to add 1 to your renown yourself in order to formulate the rule.

Farming Adventures

The renown controls some further effect. You won't find items in adventure loot whose introductory level is above your renown. Such items may drop from MP chests but campaigning won't earn you one of them (normally). Want to know if you have found all items that may drop? Add this rule:
{Introductory Level} <= 13 (find items that require renown of 13 or below)
This time you have to check against the exact renown (no further additions).

No unique cards - Build

If you decide to include a card in your build you usually do this for a reason. So why limit yourself to a single copy of each card? Let's find items (with six cards) where each card in included at least twice. I won't include the six-card check in the filter as this is quite complex as is.
@Equipment Name#0 ~> <!>( @Card Name#1 ~> <?>( [Equipment Name] == #0 and <2~6>([Card Name] == #1)))
Let's explain this piece by piece. We start on item level (as this is the information we are looking for in the end) and remember the item's name (remembering the item's id would be possible to, but as cards have an id, too, this will get hard to understand very quickly). We wanted that each card is present at least twice. This certainly applies to all cards so we use the <!> quantifier. Now we are on card level but on card level we can't check the other cards of the item. So we use a little trick and just remember the card's name. We know that there is a certain item with the active card that we are interested in, so we write exactly this: There is an item with this card and this item has the same name as the item we started with. We are finally back at the item we started with but we have another remembered name: some card of this item. So, all we have to do is to check whether at least two cards have the card name we remembered. Here, we don't ask for something like "another card with the same name", that would be impossible with the given syntax, but we just ask for "at least two with the remembered name", which, in the end, yields the same result.
You can apply the same filter to items with three cards but then the result will be items with the same card three times (you can't have two different cards at least twice on an item with only three cards), but that would be way easier to achieve.
Just think a minute about the possible combinations we would have to check without the remembering: 1==2 and 3==4 and 5==6 or 1==2 and 2==3 and 4==5 and 5==6 or ... .

Further scenarios?

Have you found other brilliant filters you want to share with the community? Write me and I'll consider to add them to this cheat sheet. Or are you trying to write a filter but it doesn't want to work as expected? Write me, too and I'll help you (and perhaps the resulted filter will be added as some kind of FAQ for others).

The Grammar - the Techy Part of the Cheat Sheet [top]

The syntax of custom filters is defined as a formal grammar. The corresponding parser is generated by the online version of PEG.js. The concrete grammar is defined as follows (if you dive deep into the grammar you'll find some further tricks):
Filter = _ Or _ / _
Or = And (_ ('|' '|'? / 'OR'i) _ And)*
And = Xor (_ ('&' '&'? / 'AND'i) _ Xor)*
Xor = Not _ ('^' / 'XOR'i) _ Not / Not
Not = ('!' / 'NOT'i) _ Not / Group
Group = '(' _ Or _ ')' / Rule
Rule = tRule / nRule / rRule / bRule
tRule = '[' Field? ']' _ [=!][=~] _ tVal
tVal = String / bID
nRule = '{' Field '}' _ nComp _ nVal
nComp = [!=]'=' / [<>]'='?
nVal = Int / bID
rRule = Quantifier _ rPred
rPred = Group / '(' _ ')'
Quantifier = '<' _ '!' _ '>' / '<' _ '?' _ '>' / '<' _ sNat _ '~' _ sNat _ '>' / '<' _ sNat _ [+-]? _ '>'
bRule = Binds _ '~>' _ Group
Binds = '@@(' _ (Bind _)+ ')' / Bind
Bind = '@' Field bID
bID "bind/bound id" = '#' Nat
Nat "natural number" = [0-9]+
sNat "signed natural number" = '-'? Nat
Int "integer" = '-'? Nat
Field "field name" = [a-zA-Z0-9_ ]+
String "string" = '"' [^\\"]* '"'
_ "spaces" = [ \t\n\r]*

Disclaimer [top]

The grammar and the necessary adaptions to the tools were defined/done by PhoenixTheHunter. Use the tools and this cheat sheet as-provided as help while playing Card Hunter. But please respect and honor the time and effort this work required by not claiming the results to be yours.

Contributors [top]

Besides PhoenixTheHunter the following community members contributed to this document and they are hereby gratefully acknowledged: