This mod is a library for parsing ItemStrings. See bottom on the developer manual.
ItemStrings are a way to describe a droptable using plain text. They let you describe what items should appear and how many in a simple, readable line. ItemStrings can include instructions for repeating items, choosing items randomly, or excluding certain items.
ItemString operators are symbols that help you decide which items to give, repeat, or exclude in a list. They make it easy to write rules like "give this item", "skip that one", or "choose one randomly" using short codes. Each entry in the ItemString roughly follows the format:
<repeat>x<itemkey>!<itemname>*<multiplier>: <weight>
Entries can be separated by & or |. Entries can be grouped with { and }.
! (Not-Operator)Tier1!Hoof - White item but not a HoofdtChest1!Bear!Crowbar - Content of a Small Chest but neither Tougher Times nor Crowbar.& (And-Operator)& will be selected.Hoof & Boss - The player receives both a Hoof and a random Boss item.| (Or-Operator)| will be chosen at random.: after the item (e.g., Tier1: 0.5 | Tier2: 0.5).Tier1 | Tier2 - The player gets either a Tier1 or a Tier2 item (equal chance unless weighted).: (Weight Separator)|).Tier1: 0.6 | Tier2: 0.4 - Tier1 has a 60% chance, Tier2 a 40% chance.Tier1: 3.0 | Tier2 - Tier1 has a 75% chance, Tier2 a 25% chance.x (Repeat-Operator)5xHoof - The player gets 5 Hoof items.3x{ Tier1 | Tier2 } - The player gets 3 times a randomly selected item from Tier1 or Tier2.* (Multiplier-Operator)Hoof*5 - The player gets 5 Hoof items.{ Tier1 | Tier2 }*3 - The player gets 3 copies of a single type of item from either Tier1 or Tier2.{ and } (Grouping Symbols)& and | within a group, but they cannot be mixed.{ } to explicitly control grouping and order of operations, especially when combining repeat, and, or multiply.{ } for more complex item generation logic.{ 2xdtChest1 | dtChest2 } - Represents a choice between "2x small chest" and "1 large chest" as a single entity; useful with other operators.| Operator | Symbol | Description | Example |
|---|---|---|---|
| Not | ! |
Blacklists the item from the group | A!B |
| And | & |
All items, together | A & B |
| Or | | |
Only one item, random (optional weight via :) |
A | B:0.7 | C:0.3 |
| Weight | : |
Specifies probability weight for options when using | |
A: 0.6 | B: 0.4 |
| Repeat | x |
Repeats the following item/group N times | 5xA or 3x{A | B} |
| Multiply | * |
Multiplies the amount of items in the group by N | {A & B}*4 |
| Grouping | {} |
Groups items/expressions | {A & 2xB} |
A random lunar item, except for Light Flux Pauldron and Stone Flux Pauldron:
ItemList = Lunar!HalfSpeedDoubleHealth!HalfAttackSpeedHalfCooldowns
5 Hooves:
ItemList = 5xHoof
Also 5 Hooves:
ItemList = Hoof*5
5 random whites:
ItemList = 5xTier1
5 copies of the same white:
ItemList = Tier1*5
5 Hooves and 2 Armor Plates:
ItemList = 5xHoof & 2xArmorPlate
Either 5 Hooves or 2 Armor Plates:
ItemList = 5xHoof | 2xArmorPlate
Either 5 Hooves with 30% chance or 2 Armor Plates with 70% chance:
ItemList = 5xHoof: 0.3 | 2xArmorPlate: 0.7
Infusion and either 5 Hooves with 30% chance or 2 Armor Plates with 70% chance:
ItemList = Infusion & { 5xHoof: 0.3 | 2xArmorPlate: 0.7 }
Either Infusion or Feather and either 5 Hooves with 30% chance or 2 Armor Plates with 70% chance:
ItemList = { Infusion | Feather } & { 5xHoof: 0.3 | 2xArmorPlate: 0.7 }
75% chance: Infusion and feather, 25% chance: 5 hoves and 5 Armor Plates
ItemList = { Infusion & Feather }: 0.75 | { Hoof & ArmorPlate }*5: 0.25
Either 5 whites or 3 greens or 1 red:
ItemList = 5xTier1 | 3xTier2 | Tier3
Between 1 and 5 whites.
ItemList = 1xTier1 | 2xTier1 | 3xTier1 | 4xTier1 | 5xTier1
10 random items which consist of around 50% whites, around 35% greens and around 15% red:
ItemList = 10x{ Tier1: 0.5 | Tier2: 0.35 | Tier3: 0.15 }
5 times one of the following: Either the contents of 2 small chests (except for Tougher Times) or the content of 1 large chest
ItemList = 5x{ 2xdtChest1!Bear | dtChest2 }
Preon Accumulator and 10 Gesture of the Drowned or Ifrit's Distinction and 1 Ignition Tank
{ BFG & 10xAutoCastEquipment } | { EliteFireEquipment & StrengthenBurn }
You can use:
Tier1, Tier2, Tier3, Lunar, Boss, VoidTier1, VoidTier2, VoidTier3, VoidBoss, FoodTierdtChest1, dtLunarChest, dtVoidChest. See below.Here is a list of supported droptables:
| Drop Table Name | tier1 | tier2 | tier3 | boss | lunarEquipment | lunarItem | lunarCombined | equipment | voidTier1 | voidTier2 | voidTier3 | voidBoss | foodTier | powerShapes | canDropBeReplaced | requiredItemTags | bannedItemTags |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| dtCasinoChest | 0.7 | 0.3 | 0.01 | 0 | 0 | 0 | 0 | 0.1 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtSmallChestDamage | 0.8 | 0.2 | 0.01 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | Damage | |
| dtSmallChestHealing | 0.8 | 0.2 | 0.01 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | Healing | |
| dtSmallChestUtility | 0.8 | 0.2 | 0.01 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | Utility | |
| dtChest1 | 0.8 | 0.2 | 0.01 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtChest2 | 0 | 0.8 | 0.2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtAISafeTier1Item | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | AIBlacklist, SprintRelated | |
| dtAISafeTier2Item | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | AIBlacklist, SprintRelated | |
| dtAISafeTier3Item | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | AIBlacklist, SprintRelated | |
| dtEquipment | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtTier1Item | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtTier2Item | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtTier3Item | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtVoidChest | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 6 | 3 | 1 | 0 | 0 | 0 | True | ||
| dtDuplicatorTier1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | CannotDuplicate | |
| dtDuplicatorTier2 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | CannotDuplicate | |
| dtDuplicatorTier3 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | CannotDuplicate | |
| dtDuplicatorWild | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | WorldUnique | |
| dtGoldChest | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtLunarChest | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtMonsterTeamTier1Item | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | AIBlacklist, OnKillEffect, EquipmentRelated, SprintRelated | |
| dtMonsterTeamTier2Item | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | AIBlacklist, OnKillEffect, EquipmentRelated, SprintRelated | |
| dtMonsterTeamTier3Item | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | AIBlacklist, OnKillEffect, EquipmentRelated, SprintRelated | |
| dtSacrificeArtifact | 0.7 | 0.3 | 0.01 | 0 | 0 | 0 | 0 | 0.1 | 0 | 0 | 0 | 0 | 0 | 0 | True | SacrificeBlacklist | |
| dtShrineChance | 8 | 2 | 0.2 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtLockbox | 0 | 4 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtCommandChest | 0.2 | 0.2 | 0.05 | 0.05 | 0 | 0 | 0 | 0.2 | 0.1 | 0.1 | 0.05 | 0.05 | 0 | 0 | True | Any, Any, Any, Any, Any | |
| dtCategoryChest2Damage | 0 | 0.8 | 0.2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | Damage | |
| dtCategoryChest2Healing | 0 | 0.8 | 0.2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | Healing | |
| dtCategoryChest2Utility | 0 | 0.8 | 0.2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | Utility | |
| dtITBossWave | 0 | 80 | 7.5 | 7.5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtITDefaultWave | 80 | 10 | 0.25 | 0.25 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtITLunar | 0 | 0 | 0 | 0 | 0 | 0 | 100 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtITSpecialBossWave | 0 | 0 | 80 | 20 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtITVoid | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 80 | 20 | 1 | 0 | 0 | 0 | True | ||
| dtVoidLockbox | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 5 | 5 | 2 | 0 | 0 | 0 | True | ||
| dtVoidCamp | 40 | 40 | 10 | 3 | 0 | 0 | 0 | 0 | 5.714286 | 5.714286 | 1.25 | 0 | 0 | 0 | True | ||
| dtVoidTriple | 0.8 | 0.2 | 0.01 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| AurelioniteHeartPickupDropTable | 0 | 0 | 0.4 | 0.6 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtShrineHalcyoniteTier1 | 0.65 | 0.3 | 0.05 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtShrineHalcyoniteTier2 | 0.65 | 0.3 | 0.05 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | HalcyoniteShrine | |
| dtShrineHalcyoniteTier3 | 0.65 | 0.3 | 0.05 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| GeodeRewardDropTable | 0.8 | 0.2 | 0.01 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtChanceDoll | 0 | 0.79 | 0.2 | 0.01 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtSonorousEcho | 0.9 | 0.1 | 0.001 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtSalvage | 0.75 | 0.25 | 0.05 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | False | CanBeTemporary | |
| dtDrifterBagChest | 0.8 | 0.2 | 0.01 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | ||
| dtJunkDrone | 0.7 | 0.25 | 0.05 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | False | CanBeTemporary | |
| dtSolusHeart | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | True | Technology | WorldUnique, CannotCopy, FoodRelated, ObjectiveRelated |
| dtTemporaryItemsDistributor | 0.8 | 0.2 | 0.01 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | False | CanBeTemporary |
You can use this EBNF to validate your item string for example at PaulKlineLabs' BNF Playground.
<ItemList> ::= <AND_LIST> | <OR_LIST>
<AND_LIST> ::= <ENTRY> (" & " <ENTRY>)*
<OR_LIST> ::= <WEIGHTED_ENTRY> (" | " <WEIGHTED_ENTRY>)*
<ENTRY> ::= (<rep> "x")? <ITEM_KEY> ("*" <mult>)?
<WEIGHTED_ENTRY> ::= <ENTRY> (": " <weight>)?
<ITEM_KEY> ::= <itemname> | <tier> ("!" <itemname>)* | <droptable> ("!" <itemname>)* | "{ " <AND_LIST> " }" | "{ " <OR_LIST> " }"
<itemname> ::= "AACannon" | "AdaptiveArmor" | "AlienHead" | "ArmorPlate" | "ArmorReductionOnHit" | "AttackSpeedAndMoveSpeed" | "AttackSpeedOnCrit" | "AttackSpeedPerNearbyAllyOrEnemy" | "AutoCastEquipment" | "Bandolier" | "BarrageOnBoss" | "BarrierOnCooldown" | "BarrierOnKill" | "BarrierOnOverHeal" | "Bear" | "BearVoid" | "BeetleGland" | "Behemoth" | "BleedOnHit" | "BleedOnHitAndExplode" | "BleedOnHitVoid" | "BonusGoldPackOnKill" | "BonusHealthBoost" | "BoostAllStats" | "BoostAttackSpeed" | "BoostDamage" | "BoostEquipmentRecharge" | "BoostHp" | "BossDamageBonus" | "BounceNearby" | "BurnNearby" | "ChainLightning" | "ChainLightningVoid" | "Clover" | "CloverVoid" | "ConvertCritChanceToCritDamage" | "CooldownOnCrit" | "CrippleWardOnLevel" | "CritAtLowerElevation" | "CritDamage" | "CritGlasses" | "CritGlassesVoid" | "CritHeal" | "Crowbar" | "CutHp" | "Dagger" | "DeathMark" | "DelayedDamage" | "DestructibleSpawner" | "DrizzlePlayerHelper" | "DroneDynamiteDisplay" | "DroneUpgradeHidden" | "DroneWeapons" | "DroneWeaponsBoost" | "DroneWeaponsDisplay1" | "DroneWeaponsDisplay2" | "DronesDropDynamite" | "Duplicator" | "ElementalRingVoid" | "EmpowerAlways" | "EnergizedOnEquipmentUse" | "EquipmentMagazine" | "EquipmentMagazineVoid" | "ExecuteLowHealthElite" | "ExplodeOnDeath" | "ExplodeOnDeathVoid" | "ExtraEquipment" | "ExtraLife" | "ExtraLifeConsumed" | "ExtraLifeVoid" | "ExtraLifeVoidConsumed" | "ExtraShrineItem" | "ExtraStatsOnLevelUp" | "FallBoots" | "Feather" | "FireRing" | "FireballsOnHit" | "Firework" | "FlatHealth" | "FocusConvergence" | "FragileDamageBonus" | "FragileDamageBonusConsumed" | "FreeChest" | "Ghost" | "GhostOnKill" | "GoldOnHit" | "GoldOnHurt" | "GummyCloneIdentifier" | "HalfAttackSpeedHalfCooldowns" | "HalfSpeedDoubleHealth" | "HeadHunter" | "HealOnCrit" | "HealWhileSafe" | "HealingPotion" | "HealingPotionConsumed" | "HealthDecay" | "Hoof" | "IceRing" | "Icicle" | "IgniteOnKill" | "ImmuneToDebuff" | "IncreaseDamageOnMultiKill" | "IncreaseHealing" | "IncreasePrimaryDamage" | "Incubator" | "Infusion" | "InvadingDoppelganger" | "ItemDropChanceOnKill" | "JumpBoost" | "JumpDamageStrike" | "Junk" | "KillEliteFrenzy" | "KnockBackHitEnemies" | "Knurl" | "LaserTurbine" | "LemurianHarness" | "LevelBonus" | "LightningStrikeOnHit" | "LowerPricedChests" | "LowerPricedChestsConsumed" | "LunarBadLuck" | "LunarDagger" | "LunarPrimaryReplacement" | "LunarSecondaryReplacement" | "LunarSpecialReplacement" | "LunarSun" | "LunarTrinket" | "LunarUtilityReplacement" | "LunarWings" | "MageAttunement" | "Medkit" | "MeteorAttackOnHighDamage" | "MinHealthPercentage" | "MinionLeash" | "MinorConstructOnKill" | "Missile" | "MissileVoid" | "MoneyLoan" | "MonsoonPlayerHelper" | "MonstersOnShrineUse" | "MoreMissile" | "MoveSpeedOnKill" | "Mushroom" | "MushroomVoid" | "NearbyDamageBonus" | "NovaOnHeal" | "NovaOnLowHealth" | "OnLevelUpFreeUnlock" | "OutOfCombatArmor" | "ParentEgg" | "PermanentDebuffOnHit" | "PersonalShield" | "Phasing" | "PhysicsProjectile" | "Plant" | "PlantOnHit" | "PlasmaCore" | "PrimarySkillShuriken" | "RandomDamageZone" | "RandomEquipmentTrigger" | "RandomlyLunar" | "RegeneratingScrap" | "RegeneratingScrapConsumed" | "RepeatHeal" | "RoboBallBuddy" | "SecondarySkillMagazine" | "Seed" | "SharedSuffering" | "ShieldBooster" | "ShieldOnly" | "ShockDamageAura" | "ShockNearby" | "SiphonOnLowHealth" | "SkullCounter" | "SlowOnHit" | "SlowOnHitVoid" | "SpeedBoostPickup" | "SpeedOnPickup" | "SprintArmor" | "SprintBonus" | "SprintOutOfCombat" | "SprintWisp" | "Squid" | "StatsFromScrap" | "StickyBomb" | "StrengthenBurn" | "StunAndPierce" | "StunChanceOnHit" | "Syringe" | "TPHealingNova" | "Talisman" | "TeamSizeDamageBonus" | "TeleportOnLowHealth" | "TeleportOnLowHealthConsumed" | "TeleportWhenOob" | "TempestOnKill" | "Thorns" | "TonicAffliction" | "Tooth" | "TransferDebuffOnHit" | "TreasureCache" | "TreasureCacheVoid" | "TriggerEnemyDebuffs" | "UseAmbientLevel" | "UtilitySkillMagazine" | "VoidMegaCrabItem" | "VoidmanPassiveItem" | "WarCryOnCombat" | "WarCryOnMultiKill" | "WardOnLevel" | "BFG" | "Blackhole" | "BossHunter" | "BurnNearby" | "Cleanse" | "CommandMissile" | "CrippleWard" | "CritOnUse" | "DeathProjectile" | "DroneBackup" | "FireBallDash" | "Fruit" | "GainArmor" | "Gateway" | "GoldGat" | "GummyClone" | "HealAndRevive" | "Jetpack" | "LifestealOnHit" | "Lightning" | "Meteor" | "Molotov" | "MultiShopCard" | "Parry" | "PassiveHealing" | "Recycle" | "Saw" | "Scanner" | "TeamWarCry" | "Tonic" | "VendingMachine"
<droptable> ::= "dtCasinoChest" | "dtSmallChestDamage" | "dtSmallChestHealing" | "dtSmallChestUtility" | "dtChest1" | "dtChest2" | "dtAISafeTier1Item" | "dtAISafeTier2Item" | "dtAISafeTier3Item" | "dtEquipment" | "dtTier1Item" | "dtTier2Item" | "dtTier3Item" | "dtVoidChest" | "dtDuplicatorTier1" | "dtDuplicatorTier2" | "dtDuplicatorTier3" | "dtDuplicatorWild" | "dtGoldChest" | "dtLunarChest" | "dtMonsterTeamTier1Item" | "dtMonsterTeamTier2Item" | "dtMonsterTeamTier3Item" | "dtSacrificeArtifact" | "dtShrineChance" | "dtLockbox" | "dtCommandChest" | "dtCategoryChest2Damage" | "dtCategoryChest2Healing" | "dtCategoryChest2Utility" | "dtITBossWave" | "dtITDefaultWave" | "dtITLunar" | "dtITSpecialBossWave" | "dtITVoid" | "dtVoidLockbox" | "dtVoidCamp" | "dtVoidTriple" | "AurelioniteHeartPickupDropTable" | "dtShrineHalcyoniteTier1" | "dtShrineHalcyoniteTier2" | "dtShrineHalcyoniteTier3" | "GeodeRewardDropTable" | "dtChanceDoll" | "dtSonorousEcho" | "dtSalvage" | "dtDrifterBagChest" | "dtJunkDrone" | "dtSolusHeart" | "dtTemporaryItemsDistributor"
<tier> ::= "Tier1" | "Tier2" | "Tier3" | "Lunar" | "Boss" | "VoidTier1" | "VoidTier2" | "VoidTier3" | "VoidBoss"
<rep> ::= <int>
<mult> ::= <int>
<weight> ::= <float>
<int> ::= [1-9] [0-9]*
<float> ::= ("0" | <int>) ("." [0-9]+ )?
Or use this one if you have modded items:
<ItemList> ::= <AND_LIST> | <OR_LIST>
<AND_LIST> ::= <ENTRY> (" & " <ENTRY>)*
<OR_LIST> ::= <WEIGHTED_ENTRY> (" | " <WEIGHTED_ENTRY>)*
<ENTRY> ::= (<rep> "x")? <ITEM_KEY> ("*" <mult>)?
<WEIGHTED_ENTRY> ::= <ENTRY> (": " <weight>)?
<ITEM_KEY> ::= <itemname> | <tier> ("!" <itemname>)* | <droptable> ("!" <itemname>)* | "{ " <AND_LIST> " }" | "{ " <OR_LIST> " }"
<itemname> ::= [a-z]+
<droptable> ::= "dtCasinoChest" | "dtSmallChestDamage" | "dtSmallChestHealing" | "dtSmallChestUtility" | "dtChest1" | "dtChest2" | "dtAISafeTier1Item" | "dtAISafeTier2Item" | "dtAISafeTier3Item" | "dtEquipment" | "dtTier1Item" | "dtTier2Item" | "dtTier3Item" | "dtVoidChest" | "dtDuplicatorTier1" | "dtDuplicatorTier2" | "dtDuplicatorTier3" | "dtDuplicatorWild" | "dtGoldChest" | "dtLunarChest" | "dtMonsterTeamTier1Item" | "dtMonsterTeamTier2Item" | "dtMonsterTeamTier3Item" | "dtSacrificeArtifact" | "dtShrineChance" | "dtLockbox" | "dtCommandChest" | "dtCategoryChest2Damage" | "dtCategoryChest2Healing" | "dtCategoryChest2Utility" | "dtITBossWave" | "dtITDefaultWave" | "dtITLunar" | "dtITSpecialBossWave" | "dtITVoid" | "dtVoidLockbox" | "dtVoidCamp" | "dtVoidTriple" | "AurelioniteHeartPickupDropTable" | "dtShrineHalcyoniteTier1" | "dtShrineHalcyoniteTier2" | "dtShrineHalcyoniteTier3" | "GeodeRewardDropTable" | "dtChanceDoll" | "dtSonorousEcho" | "dtSalvage" | "dtDrifterBagChest" | "dtJunkDrone" | "dtSolusHeart" | "dtTemporaryItemsDistributor"
<tier> ::= "Tier1" | "Tier2" | "Tier3" | "Lunar" | "Boss" | "VoidTier1" | "VoidTier2" | "VoidTier3" | "VoidBoss"
<rep> ::= <int>
<mult> ::= <int>
<weight> ::= <float>
<int> ::= [1-9] [0-9]*
<float> ::= ("0" | <int>) ("." [0-9]+ )?
As a developer if you want to use the ItemStringParser in your mod, here is what you need to do.
ItemStringParser.dll as dependency to your Visual Studio project. Set Copy Local to No so that the dll does not get copied to your build output. Example:<Reference Include="ItemStringParser">
<HintPath>libs\ItemStringParser.dll</HintPath>
<Private>false</Private>
</Reference>
[BepInDependency(ItemStringParser.ItemStringParser.PluginGUID)] to your UnityBasePlugin classDef-ItemStringParser-1.0.0 to the dependencies list in your manifest.jsonIt provides two public methods: (1) ParseItemString and (2) ResolveItemKey
Attention: The methods rely on the Run.instance.available*DropList to be populated. As such, only run the ItemStringParser after Run.BuildDropTable() was executed.
This is the main method you will want to use. This method interprets an item string, applying repetitions and other formatting rules, to build a collection of items/equipments with their amounts.
bool ItemStringParser.ItemStringParser.ParseItemString(string itemString, Dictionary<PickupIndex, int> resolvedItems, ManualLogSource log, bool availableOnly = true, int index = -1)
itemString (string): The input string containing item definitions to parse. It includes items, operators, and formatting syntax.
resolvedItems (Dictionary<PickupIndex, int>): A dictionary to which parsed item entries and their amounts will be added or updated.
log (ManualLogSource): For logging in case the provided itemString contains syntax errors.
availableOnly (bool, optional): If true will prevent concrete item names from being resolved if they are disabled or not yet unlocked.
index (int, optional): Specifies if a certain entry of the top-level or-group shall be taken or if it should be picked at random. -1 is default and means random.
Return Value: Whether it was successful
var resolvedItems = new Dictionary<PickupIndex, int>();
bool success = ItemStringParser.ItemStringParser.ParseItemString("5xHoof & {Tier1 | Tier2}*3", resolvedItems, Logger);
if (success) {
foreach (var (pickupIndex, itemAmount) in resolvedItems) {
var pickupDef = PickupCatalog.GetPickupDef(pickupIndex);
var itemIndex = pickupDef.itemIndex;
if (itemIndex != ItemIndex.None && itemAmount > 0)
{
inventory.GiveItemPermanent(itemIndex, itemAmount);
}
}
}
Equipments are also supported. Example:
var inventory = master.inventory;
Dictionary<PickupIndex, int> itemsToGive = new Dictionary<PickupIndex, int>();
bool success = ItemStringParser.ItemStringParser.ParseItemString(itemString, itemsToGive, Logger);
if(success) {
uint equipIndex = 0;
foreach (var (pickupIndex, itemAmount) in itemsToGive)
{
var pickupDef = PickupCatalog.GetPickupDef(pickupIndex);
// handle items
var itemIndex = pickupDef.itemIndex;
if (itemIndex != ItemIndex.None && itemAmount > 0)
{
inventory.GiveItemPermanent(itemIndex, itemAmount);
}
// handle equipments
int maxEquipmentSlots = master.bodyPrefab.name == "ToolbotBody" ? 2 : 1;
int maxEquipmentSets = master.inventory.GetItemCountEffective(DLC3Content.Items.ExtraEquipment.itemIndex) + 1;
int maxEquipmentCount = maxEquipmentSlots * maxEquipmentSets;
var equipmentCount = itemAmount;
var equipmentIndex = pickupDef.equipmentIndex;
while (equipmentIndex != EquipmentIndex.None && equipmentCount > 0 && equipIndex < maxEquipmentCount)
{
uint slot = (uint)(equipIndex % maxEquipmentSlots);
uint set = (uint)(equipIndex / maxEquipmentSlots);
inventory.SetEquipmentIndexForSlot(equipmentIndex, slot, set);
equipmentCount--;
equipIndex++;
}
}
}
Index can be used to retrieve the n-th entry of the root or-group. Example:
ItemStringParser.ItemStringParser.ParseItemString("Hoof | AlienHead", resolvedItems, Logger, 0); // results in "Hoof"
ItemStringParser.ItemStringParser.ParseItemString("Hoof | AlienHead", resolvedItems, Logger, 1); // results in "AlienHead"
ItemStringParser.ItemStringParser.ParseItemString("Hoof | AlienHead", resolvedItems, Logger, -1); // results in either "Hoof" or "AlienHead"
This method resolves a single item key string into a pickup index. It supports parsing item keys that represent item tiers, drop tables, concrete items, or concrete equipment. It also supports blacklisting certain items from selection. As such this method only supports the !-operator, but no other operator.
bool ItemStringParser.ItemStringParser.ResolveItemKey(string itemkey, int repeat, Dictionary<PickupIndex, int> resolvedItems, ManualLogSource log)
itemkey (string): The identifier string for the item or group of items to resolve. May exclude items from droptables or tiers with with !.
availableOnly (bool, optional): If true will prevent concrete item names from being resolved if they are disabled or not yet unlocked.
Return Value: The resolved pickupIndex.
ArgumentException: Thrown when itemkey can not be resolved to an item name, droptable or tier or when any of the blacklisted item names could not be resolved.
try
{
var pickupIndex = ItemStringParser.ResolveItemKey("Tier1!Crowbar"); // Tier1 items excluding Crowbar
}
catch(ArgumentException e)
{
Logger.LogError(e.Message);
}