df_ls_structure 0.3.0-rc.1

A language server for Dwarf Fortress RAW files
Documentation
use df_ls_core::{Choose, Clamp, DFChar, Reference, ReferenceTo, Referenceable};
use df_ls_diagnostics::DiagnosticsInfo;
use df_ls_syntax_analysis::{Token, TokenDeserialize, TryFromArgumentGroup};
use serde::{Deserialize, Serialize};

use crate::{MusicSkillEnum, SkillEnum};

#[allow(clippy::large_enum_variant)]
#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
pub enum ItemToken {
    #[token_de(token = "ITEM_AMMO")]
    AmmoToken(AmmoToken),
    #[token_de(token = "ITEM_ARMOR")]
    ArmorToken(ArmorToken),
    #[token_de(token = "ITEM_FOOD")]
    FoodToken(FoodToken),
    #[token_de(token = "ITEM_GLOVES")]
    GlovesToken(GlovesToken),
    #[token_de(token = "ITEM_HELM")]
    HelmToken(HelmToken),
    #[token_de(token = "ITEM_INSTRUMENT")]
    InstrumentToken(InstrumentToken),
    #[token_de(token = "ITEM_PANTS")]
    PantsToken(PantsToken),
    #[token_de(token = "ITEM_SHIELD")]
    ShieldToken(ShieldToken),
    #[token_de(token = "ITEM_SHOES")]
    ShoesToken(ShoesToken),
    #[token_de(token = "ITEM_SIEGEAMMO")]
    SiegeAmmoToken(SiegeAmmoToken),
    #[token_de(token = "ITEM_TOOL")]
    ToolToken(ToolToken),
    #[token_de(token = "ITEM_TOY")]
    ToyToken(ToyToken),
    #[token_de(token = "ITEM_TRAPCOMP")]
    TrapCompToken(TrapCompToken),
    #[token_de(token = "ITEM_WEAPON")]
    WeaponToken(WeaponToken),
}
impl Default for ItemToken {
    fn default() -> Self {
        Self::AmmoToken(AmmoToken::default())
    }
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct AmmoToken {
    /// Argument 1 of `[ITEM_AMMO:...]`
    #[token_de(token = "ITEM_AMMO", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    /// What this item will be called in-game.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>,
    /// This ammo can be fired from a weapon that is set to fire the same ammo type.
    /// Defaults to `BOLT`.
    #[token_de(token = "CLASS")]
    pub class: Option<Reference>,
    /// How large the ammunition is.
    #[token_de(token = "SIZE")] // Required token
    pub size: Option<u32>,
    /// The attack used by this ammo when used as a melee weapon.
    #[token_de(token = "ATTACK")]
    pub attack: Option<ItemAttack>, // TODO: test ingame if WeaponAttack could be used instead here
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct ArmorToken {
    /// Argument 1 of `[ITEM_ARMOR:...]`
    #[token_de(token = "ITEM_ARMOR", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    /// Length of the sleeves, counted in `[LIMB]` body parts towards the hands. A value of 0 only
    /// protects both halves of the torso, 1 extends over the upper arms and so on. Regardless of
    /// the value, body armor can never extend to cover the hands or head.
    ///
    /// Currently bugged, high values of `UBSTEP` will result in the item protecting
    /// facial features, fingers, and toes, while leaving those parts that it cannot protect
    /// unprotected (but still counting them as steps).
    /// [Bug:1821](http://www.bay12games.com/dwarves/mantisbt/view.php?id=1821)
    #[token_de(token = "UBSTEP")]
    pub ubstep: Option<Choose<u8, MaxEnum>>,
    // region: Shared by garments/shields/trapcomp/weapons; are all required ======================
    /// What this item will be called in-game.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>,
    /// How much material is needed to make the item. Most important with bars. The number of bars
    /// required to make the item is the value divided by three.
    #[token_de(token = "MATERIAL_SIZE")]
    pub material_size: Option<u32>,
    // endregion ==================================================================================
    // region: Shared by all garments =============================================================
    /// Adjective preceding the material name (e.g. "large copper dagger").
    #[token_de(token = "ADJECTIVE")]
    pub adjective: Option<String>,
    /// Metal versions of this item count as one `ARMORLEVEL` higher and thus won't be worn by
    /// random peasants. This tag will not work unless `ARMORLEVEL` is explicitly declared: if you
    /// leave out `ARMORLEVEL`, even metal armor will default to level 0.
    #[token_de(token = "METAL_ARMOR_LEVELS")]
    pub metal_armor_levels: Option<()>,
    /// Metal versions of this item will have "chain" added between the material and item name.
    #[token_de(token = "CHAIN_METAL_TEXT")]
    pub chain_metal_text: Option<()>,
    /// Clothiers can make this item from all kinds of cloth. If paired with `[LEATHER]`, the item
    /// has an equal chance of being either in randomly generated outfits. Further uses of this tag
    /// are unknown.
    #[token_de(token = "SOFT")]
    pub soft: Option<()>,
    /// Default state in the absence of a `[SOFT]` token. Actual effects unknown.
    #[token_de(token = "HARD")]
    pub hard: Option<()>,
    /// Item can be made from metal. Overrides `[SOFT]` and `[LEATHER]` in randomly generated
    /// outfits, if the `ARMORLEVEL` permits. Civilizations with `[WOOD_ARMOR]` will make this
    /// item out of wood instead.
    #[token_de(token = "METAL")]
    pub metal: Option<()>,
    /// Craftsmen can make this item from bones. Randomly generated outfits don't include bone
    /// armor.
    #[token_de(token = "BARRED")]
    pub barred: Option<()>,
    /// Craftsmen can make this item from shells. Randomly generated outfits don't include shell
    /// armor.
    #[token_de(token = "SCALED")]
    pub scaled: Option<()>,
    /// Leatherworkers can make this item from leather. If paired with `[SOFT]`, this item has an
    /// equal chance of being either in randomly generated outfits.
    #[token_de(token = "LEATHER")]
    pub leather: Option<()>,
    /// Only one shaped piece of clothing can be worn on a single body slot at a time.
    #[token_de(token = "SHAPED")]
    pub shaped: Option<()>,
    /// Increases the `*_STRAIN_AT_YIELD` properties of the armor's material to 50000, if lower.
    /// This makes the garment flex and give way instead of shattering under force. Strong materials
    /// that resist cutting will blunt edged attacks into bone-crushing hits instead.
    #[token_de(token = "STRUCTURAL_ELASTICITY_CHAIN_ALL")]
    pub structural_elasticity_chain_all: Option<()>,
    /// Increases the `*_STRAIN_AT_YIELD` properties of the armor's material to 50000, but only if
    /// the garment is made from metal.
    #[token_de(token = "STRUCTURAL_ELASTICITY_CHAIN_METAL")]
    pub structural_elasticity_chain_metal: Option<()>,
    /// Reduces the armor material's `SHEAR_YIELD` to 20000, `SHEAR_FRACTURE` to 30000 and increases
    /// the `*_STRAIN_AT_YIELD` properties to 50000, but only if the garment is made from cloth.
    /// This makes the item very weak against edged attacks, even if the thread material is
    /// normally very strong.
    #[token_de(token = "STRUCTURAL_ELASTICITY_WOVEN_THREAD")]
    pub structural_elasticity_woven_thread: Option<()>,
    /// The item's bulkiness when worn. Aside from the layer limitations, it's a big contributor to
    /// the thickness and weight (and therefore price) of the garment. See
    /// [Armor](https://dwarffortresswiki.org/index.php/Armor) for more on item sizes and
    /// layering. Defaults to 10.
    #[token_de(token = "LAYER_SIZE")]
    pub layer_size: Option<u32>,
    /// The maximum amount of garments that can fit underneath this garment. See
    /// [Armor](https://dwarffortresswiki.org/index.php/Armor) for more on item sizes and
    /// layering. Defaults to 10.
    #[token_de(token = "LAYER_PERMIT")]
    pub layer_permit: Option<u32>,
    /// Where the item goes in relation to other clothes. Socks cannot be worn on top of boots!
    ///
    /// The `LAYER_PERMIT` of the highest layer is used on a given section of the body - you can fit
    /// a lot of shirts and other undergarments underneath a robe, but not if you wear a leather
    /// jerkin on top of it, and you can still wear a cloak over the whole ensemble. Defaults to
    /// `UNDER`.
    #[token_de(token = "LAYER")]
    pub layer: Option<LayerEnum>,
    /// How often the garment gets in the way of a contaminant or an attack. Armor with a 5%
    /// coverage value, for example, will be near useless because 95% of attacks will bypass it
    /// completely. Temperature effects and armor thickness are also influenced. Defaults to 100.
    #[token_de(token = "COVERAGE")]
    pub coverage: Option<u8>,
    /// The garment's general purpose. Defaults to 1 for shields, 0 for everything else. Class 0
    /// items are claimed and used by civilians as ordinary clothing and are subject to wear.
    #[token_de(token = "ARMORLEVEL")]
    pub armorlevel: Option<u8>, // shared by all garments, and shields
    // endregion ==================================================================================
    // region: Shared by ARMOR and PANTS ==========================================================
    /// Changes the plural form of this item to "`phrase of` item". Primarily pertains to the stock
    /// screens.
    ///
    /// Example, "suits of" platemail, "pairs of" trousers, etc.
    #[token_de(token = "PREPLURAL")]
    pub preplural: Option<String>,
    /// If the item has no material associated with it (e.g. stockpile menus and trade
    /// negotiations), this will be displayed in its place. Used for leather armor in vanilla.
    #[token_de(token = "MATERIAL_PLACEHOLDER")]
    pub material_placeholder: Option<String>,
    /// Length of the legs/hem, counted in `[LIMB]` body parts towards the feet. A value of 0 only
    /// covers the lower body, 1 extends over the upper legs and so on. Regardless of the value,
    /// body armor or pants can never extend to cover the feet.
    #[token_de(token = "LBSTEP")]
    pub lbstep: Option<Choose<u8, MaxEnum>>,
    // endregion ==================================================================================
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct FoodToken {
    /// Argument 1 of `[ITEM_FOOD:...]`
    #[token_de(token = "ITEM_FOOD", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    /// What this item will be called in-game.
    #[token_de(token = "NAME")]
    pub name: Option<String>,
    /// Specifies the number of ingredients that are used in this type of prepared meal:
    /// - 2 for Easy. (default)
    /// - 3 for Fine.
    /// - 4 for Lavish.
    #[token_de(token = "LEVEL")]
    pub level: Option<Clamp<u8, 2, 4>>,
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct GlovesToken {
    /// Argument 1 of `[ITEM_GLOVES:...]`
    #[token_de(token = "ITEM_GLOVES", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    /// Length of gloves or footwear, counted in `[LIMB]` body parts towards the torso. A value of 1
    /// lets gloves cover the lower arms, a value of 2 stretches a boot all the way over the upper
    /// leg and so on.
    ///
    /// Regardless of the value, none of these items can ever extend to cover the upper or lower
    /// body. Shields also have this token, but it only seems to affect weight.
    #[token_de(token = "UPSTEP")]
    pub upstep: Option<Choose<u8, MaxEnum>>, // shared by glove, shield, shoes
    // region: Shared by garments/shields/trapcomp/weapons; are all required ======================
    /// What this item will be called in-game.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>,
    /// How much material is needed to make the item. Most important with bars. The number of bars
    /// required to make the item is the value divided by three.
    #[token_de(token = "MATERIAL_SIZE")]
    pub material_size: Option<u32>,
    // endregion ==================================================================================
    // region: Shared by all garments =============================================================
    /// Adjective preceding the material name (e.g. "large copper dagger").
    #[token_de(token = "ADJECTIVE")]
    pub adjective: Option<String>,
    /// Metal versions of this item count as one `ARMORLEVEL` higher and thus won't be worn by
    /// random peasants. This tag will not work unless `ARMORLEVEL` is explicitly declared: if you
    /// leave out `ARMORLEVEL`, even metal armor will default to level 0.
    #[token_de(token = "METAL_ARMOR_LEVELS")]
    pub metal_armor_levels: Option<()>,
    /// Metal versions of this item will have "chain" added between the material and item name.
    #[token_de(token = "CHAIN_METAL_TEXT")]
    pub chain_metal_text: Option<()>,
    /// Clothiers can make this item from all kinds of cloth. If paired with `[LEATHER]`, the item
    /// has an equal chance of being either in randomly generated outfits. Further uses of this tag
    /// are unknown.
    #[token_de(token = "SOFT")]
    pub soft: Option<()>,
    /// Default state in the absence of a `[SOFT]` token. Actual effects unknown.
    #[token_de(token = "HARD")]
    pub hard: Option<()>,
    /// Item can be made from metal. Overrides `[SOFT]` and `[LEATHER]` in randomly generated
    /// outfits, if the `ARMORLEVEL` permits. Civilizations with `[WOOD_ARMOR]` will make this
    /// item out of wood instead.
    #[token_de(token = "METAL")]
    pub metal: Option<()>,
    /// Craftsmen can make this item from bones. Randomly generated outfits don't include bone
    /// armor.
    #[token_de(token = "BARRED")]
    pub barred: Option<()>,
    /// Craftsmen can make this item from shells. Randomly generated outfits don't include shell
    /// armor.
    #[token_de(token = "SCALED")]
    pub scaled: Option<()>,
    /// Leatherworkers can make this item from leather. If paired with `[SOFT]`, this item has an
    /// equal chance of being either in randomly generated outfits.
    #[token_de(token = "LEATHER")]
    pub leather: Option<()>,
    /// Only one shaped piece of clothing can be worn on a single body slot at a time.
    #[token_de(token = "SHAPED")]
    pub shaped: Option<()>,
    /// Increases the `*_STRAIN_AT_YIELD` properties of the armor's material to 50000, if lower.
    /// This makes the garment flex and give way instead of shattering under force. Strong materials
    /// that resist cutting will blunt edged attacks into bone-crushing hits instead.
    #[token_de(token = "STRUCTURAL_ELASTICITY_CHAIN_ALL")]
    pub structural_elasticity_chain_all: Option<()>,
    /// Increases the `*_STRAIN_AT_YIELD` properties of the armor's material to 50000, but only if
    /// the garment is made from metal.
    #[token_de(token = "STRUCTURAL_ELASTICITY_CHAIN_METAL")]
    pub structural_elasticity_chain_metal: Option<()>,
    /// Reduces the armor material's `SHEAR_YIELD` to 20000, `SHEAR_FRACTURE` to 30000 and increases
    /// the `*_STRAIN_AT_YIELD` properties to 50000, but only if the garment is made from cloth.
    /// This makes the item very weak against edged attacks, even if the thread material is
    /// normally very strong.
    #[token_de(token = "STRUCTURAL_ELASTICITY_WOVEN_THREAD")]
    pub structural_elasticity_woven_thread: Option<()>,
    /// The item's bulkiness when worn. Aside from the layer limitations, it's a big contributor to
    /// the thickness and weight (and therefore price) of the garment. See
    /// [Armor](https://dwarffortresswiki.org/index.php/Armor) for more on item sizes and
    /// layering. Defaults to 10.
    #[token_de(token = "LAYER_SIZE")]
    pub layer_size: Option<u32>,
    /// The maximum amount of garments that can fit underneath this garment. See
    /// [Armor](https://dwarffortresswiki.org/index.php/Armor) for more on item sizes and
    /// layering. Defaults to 10.
    #[token_de(token = "LAYER_PERMIT")]
    pub layer_permit: Option<u32>,
    /// Where the item goes in relation to other clothes. Socks cannot be worn on top of boots!
    ///
    /// The `LAYER_PERMIT` of the highest layer is used on a given section of the body - you can fit
    /// a lot of shirts and other undergarments underneath a robe, but not if you wear a leather
    /// jerkin on top of it, and you can still wear a cloak over the whole ensemble. Defaults to
    /// `UNDER`.
    #[token_de(token = "LAYER")]
    pub layer: Option<LayerEnum>,
    /// How often the garment gets in the way of a contaminant or an attack. Armor with a 5%
    /// coverage value, for example, will be near useless because 95% of attacks will bypass it
    /// completely. Temperature effects and armor thickness are also influenced. Defaults to 100.
    #[token_de(token = "COVERAGE")]
    pub coverage: Option<u8>,
    /// The garment's general purpose. Defaults to 1 for shields, 0 for everything else. Class 0
    /// items are claimed and used by civilians as ordinary clothing and are subject to wear.
    #[token_de(token = "ARMORLEVEL")] // shared by all garments, and shields
    pub armorlevel: Option<u8>,
    // endregion ==================================================================================
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct HelmToken {
    /// Argument 1 of `[ITEM_HELM:...]`
    #[token_de(token = "ITEM_HELM", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    // region: Shared by garments/shields/trapcomp/weapons; are all required ======================
    /// What this item will be called in-game.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>,
    /// How much material is needed to make the item. Most important with bars. The number of bars
    /// required to make the item is the value divided by three.
    #[token_de(token = "MATERIAL_SIZE")]
    pub material_size: Option<u32>,
    // endregion ==================================================================================
    // region: Shared by all garments =============================================================
    /// Adjective preceding the material name (e.g. "large copper dagger").
    #[token_de(token = "ADJECTIVE")]
    pub adjective: Option<String>,
    /// Metal versions of this item count as one `ARMORLEVEL` higher and thus won't be worn by
    /// random peasants. This tag will not work unless `ARMORLEVEL` is explicitly declared: if you
    /// leave out `ARMORLEVEL`, even metal armor will default to level 0.
    #[token_de(token = "METAL_ARMOR_LEVELS")]
    pub metal_armor_levels: Option<()>,
    /// Metal versions of this item will have "chain" added between the material and item name.
    #[token_de(token = "CHAIN_METAL_TEXT")]
    pub chain_metal_text: Option<()>,
    /// Clothiers can make this item from all kinds of cloth. If paired with `[LEATHER]`, the item
    /// has an equal chance of being either in randomly generated outfits. Further uses of this tag
    /// are unknown.
    #[token_de(token = "SOFT")]
    pub soft: Option<()>,
    /// Default state in the absence of a `[SOFT]` token. Actual effects unknown.
    #[token_de(token = "HARD")]
    pub hard: Option<()>,
    /// Item can be made from metal. Overrides `[SOFT]` and `[LEATHER]` in randomly generated
    /// outfits, if the `ARMORLEVEL` permits. Civilizations with `[WOOD_ARMOR]` will make this
    /// item out of wood instead.
    #[token_de(token = "METAL")]
    pub metal: Option<()>,
    /// Craftsmen can make this item from bones. Randomly generated outfits don't include bone
    /// armor.
    #[token_de(token = "BARRED")]
    pub barred: Option<()>,
    /// Craftsmen can make this item from shells. Randomly generated outfits don't include shell
    /// armor.
    #[token_de(token = "SCALED")]
    pub scaled: Option<()>,
    /// Leatherworkers can make this item from leather. If paired with `[SOFT]`, this item has an
    /// equal chance of being either in randomly generated outfits.
    #[token_de(token = "LEATHER")]
    pub leather: Option<()>,
    /// Only one shaped piece of clothing can be worn on a single body slot at a time.
    #[token_de(token = "SHAPED")]
    pub shaped: Option<()>,
    /// Increases the `*_STRAIN_AT_YIELD` properties of the armor's material to 50000, if lower.
    /// This makes the garment flex and give way instead of shattering under force. Strong materials
    /// that resist cutting will blunt edged attacks into bone-crushing hits instead.
    #[token_de(token = "STRUCTURAL_ELASTICITY_CHAIN_ALL")]
    pub structural_elasticity_chain_all: Option<()>,
    /// Increases the `*_STRAIN_AT_YIELD` properties of the armor's material to 50000, but only if
    /// the garment is made from metal.
    #[token_de(token = "STRUCTURAL_ELASTICITY_CHAIN_METAL")]
    pub structural_elasticity_chain_metal: Option<()>,
    /// Reduces the armor material's `SHEAR_YIELD` to 20000, `SHEAR_FRACTURE` to 30000 and increases
    /// the `*_STRAIN_AT_YIELD` properties to 50000, but only if the garment is made from cloth.
    /// This makes the item very weak against edged attacks, even if the thread material is
    /// normally very strong.
    #[token_de(token = "STRUCTURAL_ELASTICITY_WOVEN_THREAD")]
    pub structural_elasticity_woven_thread: Option<()>,
    /// The item's bulkiness when worn. Aside from the layer limitations, it's a big contributor to
    /// the thickness and weight (and therefore price) of the garment. See
    /// [Armor](https://dwarffortresswiki.org/index.php/Armor) for more on item sizes and
    /// layering. Defaults to 10.
    #[token_de(token = "LAYER_SIZE")]
    pub layer_size: Option<u32>,
    /// The maximum amount of garments that can fit underneath this garment. See
    /// [Armor](https://dwarffortresswiki.org/index.php/Armor) for more on item sizes and
    /// layering. Defaults to 10.
    #[token_de(token = "LAYER_PERMIT")]
    pub layer_permit: Option<u32>,
    /// Where the item goes in relation to other clothes. Socks cannot be worn on top of boots!
    ///
    /// The `LAYER_PERMIT` of the highest layer is used on a given section of the body - you can fit
    /// a lot of shirts and other undergarments underneath a robe, but not if you wear a leather
    /// jerkin on top of it, and you can still wear a cloak over the whole ensemble. Defaults to
    /// `UNDER`.
    #[token_de(token = "LAYER")]
    pub layer: Option<LayerEnum>,
    /// How often the garment gets in the way of a contaminant or an attack. Armor with a 5%
    /// coverage value, for example, will be near useless because 95% of attacks will bypass it
    /// completely. Temperature effects and armor thickness are also influenced. Defaults to 100.
    #[token_de(token = "COVERAGE")]
    pub coverage: Option<u8>,
    /// The garment's general purpose. Defaults to 1 for shields, 0 for everything else. Class 0
    /// items are claimed and used by civilians as ordinary clothing and are subject to wear.
    #[token_de(token = "ARMORLEVEL")] // shared by all garments, and shields
    pub armorlevel: Option<u8>,
    // endregion ==================================================================================
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct InstrumentToken {
    /// Argument 1 of `[ITEM_INSTRUMENT:...]`
    #[token_de(token = "ITEM_INSTRUMENT", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    // region: Unique to instruments ==============================================================
    /// Makes the instrument stationary.
    #[token_de(token = "PLACED_AS_BUILDING")]
    pub placed_as_building: Option<()>,
    /// Sets a piece as the central part of the instrument.
    #[token_de(token = "DOMINANT_MATERIAL_PIECE")]
    pub dominant_material_piece: Option<Reference>,
    /// Defines an instrument piece. The 1st argument is the identifier that can be used in other
    /// raw tags to refer to this instrument piece. The 2nd is the tool which is required
    /// (and consumed) during the construction process to create this instrument piece.
    ///
    /// If an instrument does not have any pieces, `SELF` can be used for any argument which needs
    /// to be an instrument piece.
    #[token_de(token = "INSTRUMENT_PIECE")]
    pub instrument_piece: Vec<(
        Reference,
        ReferenceTo<ToolToken>,
        String,
        String,
        NameTypeEnum,
    )>,
    /// The instrument's volume range, in millibels (100 mB = 1 dB).
    #[token_de(token = "VOLUME_mB")]
    pub volume_mb: Option<(u32, u32)>,
    /// Defines how a musician can produce sound when using this instrument. Can be used multiple
    /// times.
    #[token_de(token = "SOUND_PRODUCTION")]
    pub sound_production: Vec<(SoundProductionEnum, Reference, Option<Reference>)>,
    /// Defines how the pitch can be varied by the musician. Can be used multiple times.
    #[token_de(token = "PITCH_CHOICE")]
    pub pitch_choice: Vec<(PitchMethodEnum, Reference, Option<Reference>)>,
    /// Defines how the instrument may be tuned. Can be used multiple times.
    #[token_de(token = "TUNING")]
    pub tuning: Vec<(TuningMethodEnum, Reference)>,
    /// Pitch is `min`:`max` in cents with middle C at zero. There are 1200 cents in an octave.
    ///
    /// The game verbally differentiates values from -4200 to 4200, but you can go outside
    /// that if you like.  The in-game generated instruments will range from roughly C0 to C8
    /// (-4800 to 4800), sometimes beyond for really unusual ones.
    ///
    /// You can also use `[INDEFINITE_PITCH]` instead.
    #[token_de(token = "PITCH_RANGE")]
    pub pitch_range: Option<(i32, i32)>,
    /// You can add as many timbre words as you want. The generated timbres have a series of
    /// checks for conflicts, but they don't apply to the raws, so how you use them is up to you.
    #[token_de(token = "TIMBRE")]
    pub timbre: Option<(TimbreEnum, Vec<TimbreEnum>)>,
    /// The pitch range overrides the global pitch for a register, but the register timbres are
    /// added to the global ones. You can add as many timbre words as you want.
    ///
    /// Pitch is `min`:`max` in cents with middle C at zero. There are 1200 cents in an octave.
    ///
    /// The game verbally differentiates values from -4200 to 4200, but you can go outside
    /// that if you like.  The in-game generated instruments will range from roughly C0 to C8
    /// (-4800 to 4800), sometimes beyond for really unusual ones.
    ///
    /// You can also use `[INDEFINITE_PITCH]` instead.
    #[token_de(token = "REGISTER")]
    pub register: Vec<(i32, i32, TimbreEnum, Vec<TimbreEnum>)>,
    /// The skill used for playing this instrument.
    #[token_de(token = "MUSIC_SKILL")]
    pub music_skill: Option<MusicSkillEnum>,
    /// Can be used instead of either `REGISTER` or `PITCH_RANGE`.
    #[token_de(token = "INDEFINITE_PITCH")]
    pub indefinite_pitch: Option<()>,
    // endregion ==================================================================================

    // TODO: prune the following list/region of shared tokens, these aren't all actually shared.
    // The following 3 links may be useful for figuring out which are truly shared:
    // - https://github.com/DFHack/df-structures/blob/master/df.items.xml
    // - https://github.com/DFHack/df-structures/blob/master/df.item-raws.xml
    // - https://github.com/DFHack/df-structures/blob/master/df.item-vectors.xml
    // Also, the descriptions for most of them are almost definitely inapplicable for instruments.
    // region: Shared by tools and instruments ====================================================
    /// Volume of tool in mL or cubic centimeters. Required.
    #[token_de(token = "SIZE")]
    pub size: Option<u32>,
    /// Name of the tool. Required.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>,
    /// Defines the item value of the tool. Required.
    #[token_de(token = "VALUE")]
    pub value: Option<u32>,
    /// Defines the tile used to represent the tool. Required.
    #[token_de(token = "TILE")]
    pub tile: Option<DFChar>,
    /// Permits the tool to be made from any bone.
    #[token_de(token = "BONE_MAT")]
    pub bone_mat: Option<()>,
    /// Permits the tool to be made from any ceramic material.
    #[token_de(token = "CERAMIC_MAT")]
    pub ceramic_mat: Option<()>,
    /// Allows a string to describe the tool when viewed. The text box can accommodate up to 325
    /// characters until it cuts off, but the spacing of actual sentences puts the realistic limit
    /// closer to 300.
    #[token_de(token = "DESCRIPTION")]
    pub description: Option<String>,
    /// Permits the tool to be made from any glass.
    #[token_de(token = "GLASS_MAT")]
    pub glass_mat: Option<()>,
    /// Permits the tool to be made from anything with the `[ITEMS_HARD]` token, such as wood, stone
    /// or metal.
    #[token_de(token = "HARD_MAT")]
    pub hard_mat: Option<()>,
    /// Permits the tool to be made from any leather.
    #[token_de(token = "LEATHER_MAT")]
    pub leather_mat: Option<()>,
    /// Permits the tool to be made from anything with the `[IS_METAL]` token.
    #[token_de(token = "METAL_MAT")]
    pub metal_mat: Option<()>,
    /// Permits the tool to be made from any metal with the `[ITEMS_WEAPON]` token.
    #[token_de(token = "METAL_WEAPON_MAT")]
    pub metal_weapon_mat: Option<()>,
    /// Permits the tool to be made from any "sheet" material, such as papyrus, paper, and
    /// parchment. May be connected to the `PAPER_SLURRY`/`PAPER_PLANT` reaction classes,
    /// but this is not verified.
    #[token_de(token = "SHEET_MAT")]
    pub sheet_mat: Option<()>,
    /// Permits the tool to be made from any shell.
    #[token_de(token = "SHELL_MAT")]
    pub shell_mat: Option<()>,
    /// Permits the tool to be made from any silk.
    #[token_de(token = "SILK_MAT")]
    pub silk_mat: Option<()>,
    /// Permits the tool to be made from any material with the `[ITEMS_SOFT]` token, such as leather
    /// or textiles.
    #[token_de(token = "SOFT_MAT")]
    pub soft_mat: Option<()>,
    /// Permits the tool to be made from any stone. Presumably connected to the `[IS_STONE]` token.
    #[token_de(token = "STONE_MAT")]
    pub stone_mat: Option<()>,
    /// Permits the tool to be made from any plant fiber, such as pig tails.
    #[token_de(token = "THREAD_PLANT_MAT")]
    pub thread_plant_mat: Option<()>,
    /// Permits the tool to be made from any wood.
    #[token_de(token = "WOOD_MAT")]
    pub wood_mat: Option<()>,
    /// According to Toady, "Won't be used in world gen libraries (to differentiate scrolls from
    /// quires). Also put it on bindings, rollers, instr. pieces for completeness/future use".
    /// Used on scroll rollers, book bindings, and quires.
    #[token_de(token = "INCOMPLETE_ITEM")]
    pub incomplete_item: Option<()>,
    /// Items that appear in the wild come standard with this kind of improvement. Used on scrolls:
    /// `[DEFAULT_IMPROVEMENT:SPECIFIC:ROLLERS:HARD_MAT]`
    ///
    /// Currently bugged, the effect is also applied to everything made in-game. This causes
    /// scrolls to have two sets of rollers, for example.
    #[token_de(token = "DEFAULT_IMPROVEMENT")]
    pub default_improvement: Option<ImprovementTypeWithMatFlagTokenArg>,
    /// Prevents the tool from being improved. Used on honeycombs, scroll rollers, book bindings,
    /// and quires.
    #[token_de(token = "UNIMPROVABLE")]
    pub unimprovable: Option<()>,
    /// **This token's purpose is unknown, and it may be an alias of another token; if you know
    /// what it does, please open an issue on the issue tracker.**
    #[token_de(token = "NO_DEFAULT_IMPROVEMENTS")]
    pub no_default_improvements: Option<()>,
    /// The background of the tile will be colored, instead of the foreground.
    #[token_de(token = "INVERTED_TILE")]
    pub inverted_tile: Option<()>,
    /// According to Toady, "only custom reactions are used to make this item". Found on scrolls and
    /// quires.
    #[token_de(token = "NO_DEFAULT_JOB")]
    pub no_default_job: Option<()>,
    /// Defines the task performed using the tool.
    #[token_de(token = "TOOL_USE")]
    pub tool_use: Vec<ToolUseEnum>,
    /// Allows item to be stored in a furniture stockpile.
    #[token_de(token = "FURNITURE")]
    pub furniture: Option<()>,
    // TODO: ref is shape category
    #[token_de(token = "SHAPE_CATEGORY")]
    pub shape_category: Option<Reference>,
    /// Used on dice.
    #[token_de(token = "USES_FACE_IMAGE_SET")]
    pub uses_face_image_set: Option<()>,
    /// Adjective preceding the material name (e.g. "large copper dagger")
    #[token_de(token = "ADJECTIVE")]
    pub adjective: Option<String>,
    /// How much the item can contain. Defaults to 0.
    #[token_de(token = "CONTAINER_CAPACITY")]
    pub container_capacity: Option<u32>,
    /// Required for weapons.
    #[token_de(token = "SHOOT_FORCE")]
    pub shoot_force: Option<u32>,
    /// Required for weapons.
    #[token_de(token = "SHOOT_MAXVEL")]
    pub shoot_maxvel: Option<u32>,
    /// The skill to determine effectiveness in melee with this tool. Required for weapons.
    #[token_de(token = "SKILL")]
    pub skill: Option<SkillEnum>,
    /// Makes this tool a ranged weapon that uses the specified ammo. The specified skill
    /// determines accuracy in ranged combat.
    #[token_de(token = "RANGED")]
    pub ranged: Option<(SkillEnum, ReferenceTo<AmmoToken>)>,
    /// Creatures under this size (in cm^3) must use the tool two-handed. Required for weapons.
    #[token_de(token = "TWO_HANDED")]
    pub two_handed: Option<u32>,
    /// Minimum body size (in cm^3) to use the tool at all (multigrasp required until `TWO_HANDED`
    /// value). Required for weapons.
    #[token_de(token = "MINIMUM_SIZE")]
    pub minimum_size: Option<u32>,
    /// Number of bar units needed for forging, as well as the amount gained from melting. Required
    /// for weapons.
    #[token_de(token = "MATERIAL_SIZE")]
    pub material_size: Option<u32>,
    /// You can have many `ATTACK` tags and one will be randomly selected for each attack, with
    /// `EDGE` attacks 100 times more common than `BLUNT` attacks. Required for weapons.
    #[token_de(token = "ATTACK")]
    pub attack: Vec<WeaponAttack>,
    // endregion ==================================================================================
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum NameTypeEnum {
    #[token_de(token = "STANDARD")]
    Standard,
    #[token_de(token = "ALWAYS_PLURAL")]
    AlwaysPlural,
    #[token_de(token = "ALWAYS_SINGULAR")]
    AlwaysSingular,
}
impl Default for NameTypeEnum {
    fn default() -> Self {
        Self::Standard
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum PitchMethodEnum {
    #[token_de(token = "MEMBRANE_POSITION")]
    MembranePosition,
    #[token_de(token = "SUBPART_CHOICE")]
    SubpartChoice,
    #[token_de(token = "KEYBOARD")]
    Keyboard,
    /// Requires two `INSTRUMENT_PIECE` tokens, first for "string" second for "neck"
    /// -- or whatever is being pressed against what.
    #[token_de(token = "STOPPING_FRET")]
    StoppingFret,
    /// Requires two `INSTRUMENT_PIECE` tokens.
    #[token_de(token = "STOPPING_AGAINST_BODY")]
    StoppingAgainstBody,
    #[token_de(token = "STOPPING_HOLE")]
    StoppingHole,
    #[token_de(token = "STOPPING_HOLE_KEY")]
    StoppingHoleKey,
    #[token_de(token = "SLIDE")]
    Slide,
    #[token_de(token = "HARMONIC_SERIES")]
    HarmonicSeries,
    #[token_de(token = "VALVE_ROUTES_AIR")]
    ValveRoutesAir,
    #[token_de(token = "BP_IN_BELL")]
    BpInBell,
    /// Requires two `INSTRUMENT_PIECE` tokens, first is what is being changed e.g. "strings",
    /// second is "body" which has the pedalboard -- or whatever piece is being stepped on.
    #[token_de(token = "FOOT_PEDALS")]
    FootPedals,
}
impl Default for PitchMethodEnum {
    fn default() -> Self {
        Self::MembranePosition
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum TuningMethodEnum {
    #[token_de(token = "PEGS")]
    Pegs,
    #[token_de(token = "ADJUSTABLE_BRIDGES")]
    AdjustableBridhes,
    #[token_de(token = "CROOKS")]
    Crooks,
    #[token_de(token = "TIGHTENING")]
    Tightening,
    #[token_de(token = "LEVERS")]
    Levers,
}
impl Default for TuningMethodEnum {
    fn default() -> Self {
        Self::Pegs
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum SoundProductionEnum {
    #[token_de(token = "PLUCKED_BY_BP")]
    PluckedByBp,
    /// Requires two `INSTRUMENT_PIECE` tokens: actor, then target.
    #[token_de(token = "PLUCKED")]
    Plucked,
    /// Requires two `INSTRUMENT_PIECE` tokens: actor, then target.
    #[token_de(token = "BOWED")]
    Bowed,
    #[token_de(token = "STRUCK_BY_BP")]
    StruckByBp,
    /// Requires two `INSTRUMENT_PIECE` tokens: actor, then target.
    #[token_de(token = "STRUCK")]
    Struck,
    #[token_de(token = "VIBRATE_BP_AGAINST_OPENING")]
    VibrateBpAgainstOpening,
    #[token_de(token = "BLOW_AGAINST_FIPPLE")]
    BlowAgainstFipple,
    #[token_de(token = "BLOW_OVER_OPENING_SIDE")]
    BlowOverOpeningSide,
    #[token_de(token = "BLOW_OVER_OPENING_END")]
    BlowOverOpeningEnd,
    #[token_de(token = "BLOW_OVER_SINGLE_REED")]
    BlowOverSingleReed,
    #[token_de(token = "BLOW_OVER_DOUBLE_REED")]
    BlowOverDoubleReed,
    #[token_de(token = "BLOW_OVER_FREE_REED")]
    BlowOverFreeReed,
    #[token_de(token = "STRUCK_TOGETHER")]
    StruckTogether,
    #[token_de(token = "SHAKEN")]
    Shaken,
    /// Requires two `INSTRUMENT_PIECE` tokens: actor, then target.
    #[token_de(token = "SCRAPED")]
    Scraped,
    /// Requires two `INSTRUMENT_PIECE` tokens: actor, then target.
    #[token_de(token = "FRICTION")]
    Friction,
    #[token_de(token = "RESONATOR")]
    Resonator,
    /// Requires two `INSTRUMENT_PIECE` tokens: actor, then target.
    #[token_de(token = "BAG_OVER_REED")]
    BagOverReed,
    /// Requires two `INSTRUMENT_PIECE` tokens: actor, then target.
    #[token_de(token = "AIR_OVER_REED")]
    AirOverReed,
    /// Requires two `INSTRUMENT_PIECE` tokens: actor, then target.
    #[token_de(token = "AIR_OVER_FREE_REED")]
    AirOverFreeReed,
    /// Requires two `INSTRUMENT_PIECE` tokens: actor, then target.
    #[token_de(token = "AIR_AGAINST_FIPPLE")]
    AirAgainstFipple,
}
impl Default for SoundProductionEnum {
    fn default() -> Self {
        Self::PluckedByBp
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum TimbreEnum {
    #[token_de(token = "CLEAR")]
    Clear,
    #[token_de(token = "NOISY")]
    Noisy,
    #[token_de(token = "FULL")]
    Full,
    #[token_de(token = "THIN")]
    Thin,
    #[token_de(token = "ROUND")]
    Round,
    #[token_de(token = "SHARP")]
    Sharp,
    #[token_de(token = "SMOOTH")]
    Smooth,
    #[token_de(token = "CHOPPY")]
    Choppy,
    #[token_de(token = "STEADY")]
    Steady,
    #[token_de(token = "EVOLVING")]
    Evolving,
    #[token_de(token = "STRONG")]
    Strong,
    #[token_de(token = "DELICATE")]
    Delicate,
    #[token_de(token = "BRIGHT")]
    Bright,
    #[token_de(token = "GRACEFUL")]
    Graceful,
    #[token_de(token = "SPARSE")]
    Sparse,
    #[token_de(token = "BREATHY")]
    Breathy,
    #[token_de(token = "STRAINED")]
    Strained,
    #[token_de(token = "BROAD")]
    Broad,
    #[token_de(token = "LIGHT")]
    Light,
    #[token_de(token = "MELLOW")]
    Mellow,
    #[token_de(token = "WOBBLING")]
    Wobbling,
    #[token_de(token = "FOCUSED")]
    Focused,
    #[token_de(token = "EVEN")]
    Even,
    #[token_de(token = "FLUID")]
    Fluid,
    #[token_de(token = "VIBRATING")]
    Vibrating,
    #[token_de(token = "QUAVERING")]
    Quavering,
    #[token_de(token = "EERIE")]
    Eerie,
    #[token_de(token = "FRAGILE")]
    Fragile,
    #[token_de(token = "BRITTLE")]
    Brittle,
    #[token_de(token = "PURE")]
    Pure,
    #[token_de(token = "PIERCING")]
    Piercing,
    #[token_de(token = "STRIDENT")]
    Strident,
    #[token_de(token = "WAVERING")]
    Wavering,
    #[token_de(token = "HARSH")]
    Harsh,
    #[token_de(token = "REEDY")]
    Reedy,
    #[token_de(token = "NASAL")]
    Nasal,
    #[token_de(token = "BUZZY")]
    Buzzy,
    #[token_de(token = "ROUGH")]
    Rough,
    #[token_de(token = "WARM")]
    Warm,
    #[token_de(token = "RUGGED")]
    Rugged,
    #[token_de(token = "HEAVY")]
    Heavy,
    #[token_de(token = "FLAT")]
    Flat,
    #[token_de(token = "DARK")]
    Dark,
    #[token_de(token = "CRISP")]
    Crisp,
    #[token_de(token = "SONOROUS")]
    Sonorous,
    #[token_de(token = "WATERY")]
    Watery,
    #[token_de(token = "GENTLE")]
    Gentle,
    #[token_de(token = "SLICING")]
    Slicing,
    #[token_de(token = "LIQUID")]
    Liquid,
    #[token_de(token = "RAUCOUS")]
    Raucous,
    #[token_de(token = "BREEZY")]
    Breezy,
    #[token_de(token = "RASPY")]
    Raspy,
    #[token_de(token = "WISPY")]
    Wispy,
    #[token_de(token = "SHRILL")]
    Shrill,
    #[token_de(token = "MUDDY")]
    Muddy,
    #[token_de(token = "RICH")]
    Rich,
    #[token_de(token = "DULL")]
    Dull,
    #[token_de(token = "FLOATING")]
    Floating,
    #[token_de(token = "RINGING")]
    Ringing,
    #[token_de(token = "RESONANT")]
    Resonant,
    #[token_de(token = "SWEET")]
    Sweet,
    #[token_de(token = "RIPPLING")]
    Rippling,
    #[token_de(token = "SPARKLING")]
    Sparkling,
}
impl Default for TimbreEnum {
    fn default() -> Self {
        Self::Clear
    }
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct PantsToken {
    /// Argument 1 of `[ITEM_PANTS:...]`
    #[token_de(token = "ITEM_PANTS", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    // region: Shared by garments/shields/trapcomp/weapons; are all required ======================
    /// What this item will be called in-game.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>,
    /// How much material is needed to make the item. Most important with bars. The number of bars
    /// required to make the item is the value divided by three.
    #[token_de(token = "MATERIAL_SIZE")]
    pub material_size: Option<u32>,
    // endregion ==================================================================================
    // region: Shared by all garments =============================================================
    /// Adjective preceding the material name (e.g. "large copper dagger").
    #[token_de(token = "ADJECTIVE")]
    pub adjective: Option<String>,
    /// Metal versions of this item count as one `ARMORLEVEL` higher and thus won't be worn by
    /// random peasants. This tag will not work unless `ARMORLEVEL` is explicitly declared: if you
    /// leave out `ARMORLEVEL`, even metal armor will default to level 0.
    #[token_de(token = "METAL_ARMOR_LEVELS")]
    pub metal_armor_levels: Option<()>,
    /// Metal versions of this item will have "chain" added between the material and item name.
    #[token_de(token = "CHAIN_METAL_TEXT")]
    pub chain_metal_text: Option<()>,
    /// Clothiers can make this item from all kinds of cloth. If paired with `[LEATHER]`, the item
    /// has an equal chance of being either in randomly generated outfits. Further uses of this tag
    /// are unknown.
    #[token_de(token = "SOFT")]
    pub soft: Option<()>,
    /// Default state in the absence of a `[SOFT]` token. Actual effects unknown.
    #[token_de(token = "HARD")]
    pub hard: Option<()>,
    /// Item can be made from metal. Overrides `[SOFT]` and `[LEATHER]` in randomly generated
    /// outfits, if the `ARMORLEVEL` permits. Civilizations with `[WOOD_ARMOR]` will make this
    /// item out of wood instead.
    #[token_de(token = "METAL")]
    pub metal: Option<()>,
    /// Craftsmen can make this item from bones. Randomly generated outfits don't include bone
    /// armor.
    #[token_de(token = "BARRED")]
    pub barred: Option<()>,
    /// Craftsmen can make this item from shells. Randomly generated outfits don't include shell
    /// armor.
    #[token_de(token = "SCALED")]
    pub scaled: Option<()>,
    /// Leatherworkers can make this item from leather. If paired with `[SOFT]`, this item has an
    /// equal chance of being either in randomly generated outfits.
    #[token_de(token = "LEATHER")]
    pub leather: Option<()>,
    /// Only one shaped piece of clothing can be worn on a single body slot at a time.
    #[token_de(token = "SHAPED")]
    pub shaped: Option<()>,
    /// Increases the `*_STRAIN_AT_YIELD` properties of the armor's material to 50000, if lower.
    /// This makes the garment flex and give way instead of shattering under force. Strong materials
    /// that resist cutting will blunt edged attacks into bone-crushing hits instead.
    #[token_de(token = "STRUCTURAL_ELASTICITY_CHAIN_ALL")]
    pub structural_elasticity_chain_all: Option<()>,
    /// Increases the `*_STRAIN_AT_YIELD` properties of the armor's material to 50000, but only if
    /// the garment is made from metal.
    #[token_de(token = "STRUCTURAL_ELASTICITY_CHAIN_METAL")]
    pub structural_elasticity_chain_metal: Option<()>,
    /// Reduces the armor material's `SHEAR_YIELD` to 20000, `SHEAR_FRACTURE` to 30000 and increases
    /// the `*_STRAIN_AT_YIELD` properties to 50000, but only if the garment is made from cloth.
    /// This makes the item very weak against edged attacks, even if the thread material is
    /// normally very strong.
    #[token_de(token = "STRUCTURAL_ELASTICITY_WOVEN_THREAD")]
    pub structural_elasticity_woven_thread: Option<()>,
    /// The item's bulkiness when worn. Aside from the layer limitations, it's a big contributor to
    /// the thickness and weight (and therefore price) of the garment. See
    /// [Armor](https://dwarffortresswiki.org/index.php/Armor) for more on item sizes and
    /// layering. Defaults to 10.
    #[token_de(token = "LAYER_SIZE")]
    pub layer_size: Option<u32>,
    /// The maximum amount of garments that can fit underneath this garment. See
    /// [Armor](https://dwarffortresswiki.org/index.php/Armor) for more on item sizes and
    /// layering. Defaults to 10.
    #[token_de(token = "LAYER_PERMIT")]
    pub layer_permit: Option<u32>,
    /// Where the item goes in relation to other clothes. Socks cannot be worn on top of boots!
    ///
    /// The `LAYER_PERMIT` of the highest layer is used on a given section of the body - you can fit
    /// a lot of shirts and other undergarments underneath a robe, but not if you wear a leather
    /// jerkin on top of it, and you can still wear a cloak over the whole ensemble. Defaults to
    /// `UNDER`.
    #[token_de(token = "LAYER")]
    pub layer: Option<LayerEnum>,
    /// How often the garment gets in the way of a contaminant or an attack. Armor with a 5%
    /// coverage value, for example, will be near useless because 95% of attacks will bypass it
    /// completely. Temperature effects and armor thickness are also influenced. Defaults to 100.
    #[token_de(token = "COVERAGE")]
    pub coverage: Option<u8>,
    /// The garment's general purpose. Defaults to 1 for shields, 0 for everything else. Class 0
    /// items are claimed and used by civilians as ordinary clothing and are subject to wear.
    #[token_de(token = "ARMORLEVEL")] // shared by all garments, and shields
    pub armorlevel: Option<u8>,
    // endregion ==================================================================================
    // region: Shared by ARMOR and PANTS ==========================================================
    /// Changes the plural form of this item to "`phrase of` item". Primarily pertains to the stock
    /// screens.
    ///
    /// Example, "suits of" platemail, "pairs of" trousers, etc.
    #[token_de(token = "PREPLURAL")]
    pub preplural: Option<String>,
    /// If the item has no material associated with it (e.g. stockpile menus and trade
    /// negotiations), this will be displayed in its place. Used for leather armor in vanilla.
    #[token_de(token = "MATERIAL_PLACEHOLDER")]
    pub material_placeholder: Option<String>,
    /// Length of the legs/hem, counted in `[LIMB]` body parts towards the feet. A value of 0 only
    /// covers the lower body, 1 extends over the upper legs and so on. Regardless of the value,
    /// body armor or pants can never extend to cover the feet.
    #[token_de(token = "LBSTEP")]
    pub lbstep: Option<Choose<u8, MaxEnum>>,
    // endregion ==================================================================================
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct ShieldToken {
    /// Argument 1 of `[ITEM_SHIELD:...]`
    #[token_de(token = "ITEM_SHIELD", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    /// Affects the block chance of the shield. Defaults to 10.
    #[token_de(token = "BLOCKCHANCE")]
    pub blockchance: Option<Clamp<u8, 0, 100>>,
    /// Length of gloves or footwear, counted in `[LIMB]` body parts towards the torso. A value of 1
    /// lets gloves cover the lower arms, a value of 2 stretches a boot all the way over the upper
    /// leg and so on.
    ///
    /// Regardless of the value, none of these items can ever extend to cover the upper or lower
    /// body. Shields also have this token, but it only seems to affect weight.
    #[token_de(token = "UPSTEP")]
    pub upstep: Option<Choose<u8, MaxEnum>>, // shared by glove, shield, shoes
    /// The garment's general purpose. Defaults to 1 for shields, 0 for everything else. Class 0
    /// items are claimed and used by civilians as ordinary clothing and are subject to wear.
    #[token_de(token = "ARMORLEVEL")]
    pub armorlevel: Option<u8>, // shared by all garments, and shields
    // region: Shared by garments/shields/trapcomp/weapons; are all required ======================
    /// What this item will be called in-game.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>,
    /// How much material is needed to make the item. Most important with bars. The number of bars
    /// required to make the item is the value divided by three.
    #[token_de(token = "MATERIAL_SIZE")]
    pub material_size: Option<u32>,
    // endregion ==================================================================================
    /// Adjective preceding the material name (e.g. "large copper dagger").
    #[token_de(token = "ADJECTIVE")]
    pub adjective: Option<String>,
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct ShoesToken {
    /// Argument 1 of `[ITEM_SHOES:...]`
    #[token_de(token = "ITEM_SHOES", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    /// Length of gloves or footwear, counted in `[LIMB]` body parts towards the torso. A value of 1
    /// lets gloves cover the lower arms, a value of 2 stretches a boot all the way over the upper
    /// leg and so on.
    ///
    /// Regardless of the value, none of these items can ever extend to cover the upper or lower
    /// body. Shields also have this token, but it only seems to affect weight.
    #[token_de(token = "UPSTEP")]
    pub upstep: Option<Choose<u8, MaxEnum>>, // shared by glove, shield, shoes
    // region: Shared by garments/shields/trapcomp/weapons; are all required ======================
    /// What this item will be called in-game.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>,
    /// How much material is needed to make the item. Most important with bars. The number of bars
    /// required to make the item is the value divided by three.
    #[token_de(token = "MATERIAL_SIZE")]
    pub material_size: Option<u32>,
    // endregion ==================================================================================
    // region: Shared by all garments =============================================================
    /// Adjective preceding the material name (e.g. "large copper dagger").
    #[token_de(token = "ADJECTIVE")]
    pub adjective: Option<String>,
    /// Metal versions of this item count as one `ARMORLEVEL` higher and thus won't be worn by
    /// random peasants. This tag will not work unless `ARMORLEVEL` is explicitly declared: if you
    /// leave out `ARMORLEVEL`, even metal armor will default to level 0.
    #[token_de(token = "METAL_ARMOR_LEVELS")]
    pub metal_armor_levels: Option<()>,
    /// Metal versions of this item will have "chain" added between the material and item name.
    #[token_de(token = "CHAIN_METAL_TEXT")]
    pub chain_metal_text: Option<()>,
    /// Clothiers can make this item from all kinds of cloth. If paired with `[LEATHER]`, the item
    /// has an equal chance of being either in randomly generated outfits. Further uses of this tag
    /// are unknown.
    #[token_de(token = "SOFT")]
    pub soft: Option<()>,
    /// Default state in the absence of a `[SOFT]` token. Actual effects unknown.
    #[token_de(token = "HARD")]
    pub hard: Option<()>,
    /// Item can be made from metal. Overrides `[SOFT]` and `[LEATHER]` in randomly generated
    /// outfits, if the `ARMORLEVEL` permits. Civilizations with `[WOOD_ARMOR]` will make this
    /// item out of wood instead.
    #[token_de(token = "METAL")]
    pub metal: Option<()>,
    /// Craftsmen can make this item from bones. Randomly generated outfits don't include bone
    /// armor.
    #[token_de(token = "BARRED")]
    pub barred: Option<()>,
    /// Craftsmen can make this item from shells. Randomly generated outfits don't include shell
    /// armor.
    #[token_de(token = "SCALED")]
    pub scaled: Option<()>,
    /// Leatherworkers can make this item from leather. If paired with `[SOFT]`, this item has an
    /// equal chance of being either in randomly generated outfits.
    #[token_de(token = "LEATHER")]
    pub leather: Option<()>,
    /// Only one shaped piece of clothing can be worn on a single body slot at a time.
    #[token_de(token = "SHAPED")]
    pub shaped: Option<()>,
    /// Increases the `*_STRAIN_AT_YIELD` properties of the armor's material to 50000, if lower.
    /// This makes the garment flex and give way instead of shattering under force. Strong materials
    /// that resist cutting will blunt edged attacks into bone-crushing hits instead.
    #[token_de(token = "STRUCTURAL_ELASTICITY_CHAIN_ALL")]
    pub structural_elasticity_chain_all: Option<()>,
    /// Increases the `*_STRAIN_AT_YIELD` properties of the armor's material to 50000, but only if
    /// the garment is made from metal.
    #[token_de(token = "STRUCTURAL_ELASTICITY_CHAIN_METAL")]
    pub structural_elasticity_chain_metal: Option<()>,
    /// Reduces the armor material's `SHEAR_YIELD` to 20000, `SHEAR_FRACTURE` to 30000 and increases
    /// the `*_STRAIN_AT_YIELD` properties to 50000, but only if the garment is made from cloth.
    /// This makes the item very weak against edged attacks, even if the thread material is
    /// normally very strong.
    #[token_de(token = "STRUCTURAL_ELASTICITY_WOVEN_THREAD")]
    pub structural_elasticity_woven_thread: Option<()>,
    /// The item's bulkiness when worn. Aside from the layer limitations, it's a big contributor to
    /// the thickness and weight (and therefore price) of the garment. See
    /// [Armor](https://dwarffortresswiki.org/index.php/Armor) for more on item sizes and
    /// layering. Defaults to 10.
    #[token_de(token = "LAYER_SIZE")]
    pub layer_size: Option<u32>,
    /// The maximum amount of garments that can fit underneath this garment. See
    /// [Armor](https://dwarffortresswiki.org/index.php/Armor) for more on item sizes and
    /// layering. Defaults to 10.
    #[token_de(token = "LAYER_PERMIT")]
    pub layer_permit: Option<u32>,
    /// Where the item goes in relation to other clothes. Socks cannot be worn on top of boots!
    ///
    /// The `LAYER_PERMIT` of the highest layer is used on a given section of the body - you can fit
    /// a lot of shirts and other undergarments underneath a robe, but not if you wear a leather
    /// jerkin on top of it, and you can still wear a cloak over the whole ensemble. Defaults to
    /// `UNDER`.
    #[token_de(token = "LAYER")]
    pub layer: Option<LayerEnum>,
    /// How often the garment gets in the way of a contaminant or an attack. Armor with a 5%
    /// coverage value, for example, will be near useless because 95% of attacks will bypass it
    /// completely. Temperature effects and armor thickness are also influenced. Defaults to 100.
    #[token_de(token = "COVERAGE")]
    pub coverage: Option<u8>,
    /// The garment's general purpose. Defaults to 1 for shields, 0 for everything else. Class 0
    /// items are claimed and used by civilians as ordinary clothing and are subject to wear.
    #[token_de(token = "ARMORLEVEL")] // shared by all garments, and shields
    pub armorlevel: Option<u8>,
    // endregion ==================================================================================
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum LayerEnum {
    #[token_de(token = "UNDER")]
    Under,
    #[token_de(token = "OVER")]
    Over,
    #[token_de(token = "ARMOR")]
    Armor,
    #[token_de(token = "COVER")]
    Cover,
}
impl Default for LayerEnum {
    fn default() -> Self {
        Self::Under
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum MaxEnum {
    #[token_de(token = "MAX")]
    Max,
}
impl Default for MaxEnum {
    fn default() -> Self {
        Self::Max
    }
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct SiegeAmmoToken {
    /// Argument 1 of `[ITEM_SIEGEAMMO:...]`
    #[token_de(token = "ITEM_SIEGEAMMO", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    /// What this item will be called in-game.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>,
    /// Specifies what type of siege engine uses this ammunition. Currently, only `BALLISTA` is
    /// permitted.
    #[token_de(token = "CLASS")]
    pub class: Option<SiegeAmmoClassEnum>,
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum SiegeAmmoClassEnum {
    #[token_de(token = "BALLISTA")]
    Ballista,
}
impl Default for SiegeAmmoClassEnum {
    fn default() -> Self {
        Self::Ballista
    }
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct ToolToken {
    /// Argument 1 of `[ITEM_TOOL:...]`
    #[token_de(token = "ITEM_TOOL", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    /// Volume of tool in mL or cubic centimeters. Required.
    #[token_de(token = "SIZE")]
    pub size: Option<u32>,
    /// Name of the tool. Required.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>,
    /// Defines the item value of the tool. Required.
    #[token_de(token = "VALUE")]
    pub value: Option<u32>,
    /// Defines the tile used to represent the tool. Required.
    #[token_de(token = "TILE")]
    pub tile: Option<DFChar>,
    /// Permits the tool to be made from any bone.
    #[token_de(token = "BONE_MAT")]
    pub bone_mat: Option<()>,
    /// Permits the tool to be made from any ceramic material.
    #[token_de(token = "CERAMIC_MAT")]
    pub ceramic_mat: Option<()>,
    /// Allows a string to describe the tool when viewed. The text box can accommodate up to 325
    /// characters until it cuts off, but the spacing of actual sentences puts the realistic limit
    /// closer to 300.
    #[token_de(token = "DESCRIPTION")]
    pub description: Option<String>,
    /// Permits the tool to be made from any glass.
    #[token_de(token = "GLASS_MAT")]
    pub glass_mat: Option<()>,
    /// Permits the tool to be made from anything with the `[ITEMS_HARD]` token, such as wood, stone
    /// or metal.
    #[token_de(token = "HARD_MAT")]
    pub hard_mat: Option<()>,
    /// Permits the tool to be made from any leather.
    #[token_de(token = "LEATHER_MAT")]
    pub leather_mat: Option<()>,
    /// Permits the tool to be made from anything with the `[IS_METAL]` token.
    #[token_de(token = "METAL_MAT")]
    pub metal_mat: Option<()>,
    /// Permits the tool to be made from any metal with the `[ITEMS_WEAPON]` token.
    #[token_de(token = "METAL_WEAPON_MAT")]
    pub metal_weapon_mat: Option<()>,
    /// Permits the tool to be made from any "sheet" material, such as papyrus, paper, and
    /// parchment. May be connected to the `PAPER_SLURRY`/`PAPER_PLANT` reaction classes,
    /// but this is not verified.
    #[token_de(token = "SHEET_MAT")]
    pub sheet_mat: Option<()>,
    /// Permits the tool to be made from any shell.
    #[token_de(token = "SHELL_MAT")]
    pub shell_mat: Option<()>,
    /// Permits the tool to be made from any silk.
    #[token_de(token = "SILK_MAT")]
    pub silk_mat: Option<()>,
    /// Permits the tool to be made from any material with the `[ITEMS_SOFT]` token, such as leather
    /// or textiles.
    #[token_de(token = "SOFT_MAT")]
    pub soft_mat: Option<()>,
    /// Permits the tool to be made from any stone. Presumably connected to the `[IS_STONE]` token.
    #[token_de(token = "STONE_MAT")]
    pub stone_mat: Option<()>,
    /// Permits the tool to be made from any plant fiber, such as pig tails.
    #[token_de(token = "THREAD_PLANT_MAT")]
    pub thread_plant_mat: Option<()>,
    /// Permits the tool to be made from any wood.
    #[token_de(token = "WOOD_MAT")]
    pub wood_mat: Option<()>,
    /// According to Toady, "Won't be used in world gen libraries (to differentiate scrolls from
    /// quires). Also put it on bindings, rollers, instr. pieces for completeness/future use".
    /// Used on scroll rollers, book bindings, and quires.
    #[token_de(token = "INCOMPLETE_ITEM")]
    pub incomplete_item: Option<()>,
    /// Items that appear in the wild come standard with this kind of improvement. Used on scrolls:
    /// `[DEFAULT_IMPROVEMENT:SPECIFIC:ROLLERS:HARD_MAT]`
    ///
    /// Currently bugged, the effect is also applied to everything made in-game. This causes
    /// scrolls to have two sets of rollers, for example.
    #[token_de(token = "DEFAULT_IMPROVEMENT")]
    pub default_improvement: Option<ImprovementTypeWithMatFlagTokenArg>,
    /// Prevents the tool from being improved. Used on honeycombs, scroll rollers, book bindings,
    /// and quires.
    #[token_de(token = "UNIMPROVABLE")]
    pub unimprovable: Option<()>,
    /// **This token's purpose is unknown, and it may be an alias of another token; if you know
    /// what it does, please open an issue on the issue tracker.**
    #[token_de(token = "NO_DEFAULT_IMPROVEMENTS")]
    pub no_default_improvements: Option<()>,
    /// The background of the tile will be colored, instead of the foreground.
    #[token_de(token = "INVERTED_TILE")]
    pub inverted_tile: Option<()>,
    /// According to Toady, "only custom reactions are used to make this item". Found on scrolls and
    /// quires.
    #[token_de(token = "NO_DEFAULT_JOB")]
    pub no_default_job: Option<()>,
    /// Defines the task performed using the tool.
    #[token_de(token = "TOOL_USE")]
    pub tool_use: Vec<ToolUseEnum>,
    /// Allows item to be stored in a furniture stockpile.
    #[token_de(token = "FURNITURE")]
    pub furniture: Option<()>,
    // TODO: ref is shape category
    #[token_de(token = "SHAPE_CATEGORY")]
    pub shape_category: Option<Reference>,
    /// Used on dice.
    #[token_de(token = "USES_FACE_IMAGE_SET")]
    pub uses_face_image_set: Option<()>,
    /// Adjective preceding the material name (e.g. "large copper dagger")
    #[token_de(token = "ADJECTIVE")]
    pub adjective: Option<String>,
    /// How much the item can contain. Defaults to 0.
    #[token_de(token = "CONTAINER_CAPACITY")]
    pub container_capacity: Option<u32>,
    /// Required for weapons.
    #[token_de(token = "SHOOT_FORCE")]
    pub shoot_force: Option<u32>,
    /// Required for weapons.
    #[token_de(token = "SHOOT_MAXVEL")]
    pub shoot_maxvel: Option<u32>,
    /// The skill to determine effectiveness in melee with this tool. Required for weapons.
    #[token_de(token = "SKILL")]
    pub skill: Option<SkillEnum>,
    /// Makes this tool a ranged weapon that uses the specified ammo. The specified skill
    /// determines accuracy in ranged combat.
    #[token_de(token = "RANGED")]
    pub ranged: Option<(SkillEnum, ReferenceTo<AmmoToken>)>,
    /// Creatures under this size (in cm^3) must use the tool two-handed. Required for weapons.
    #[token_de(token = "TWO_HANDED")]
    pub two_handed: Option<u32>,
    /// Minimum body size (in cm^3) to use the tool at all (multigrasp required until `TWO_HANDED`
    /// value). Required for weapons.
    #[token_de(token = "MINIMUM_SIZE")]
    pub minimum_size: Option<u32>,
    /// Number of bar units needed for forging, as well as the amount gained from melting. Required
    /// for weapons.
    #[token_de(token = "MATERIAL_SIZE")]
    pub material_size: Option<u32>,
    /// You can have many `ATTACK` tags and one will be randomly selected for each attack, with
    /// `EDGE` attacks 100 times more common than `BLUNT` attacks. Required for weapons.
    #[token_de(token = "ATTACK")]
    pub attack: Vec<WeaponAttack>,
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum ToolUseEnum {
    /// Cauldron adventure mode decoration / weapon
    #[token_de(token = "LIQUID_COOKING")]
    LiquidCooking,
    /// Ladle, an adventure mode decoration/weapon.
    #[token_de(token = "LIQUID_SCOOP")]
    LiquidScoop,
    /// Mortar, an adventure mode decoration/weapon.
    #[token_de(token = "GRIND_POWDER_RECEPTACLE")]
    GrindPowderReceptacle,
    /// Pestle, an adventure mode decoration/weapon.
    #[token_de(token = "GRIND_POWDER_GRINDER")]
    GrindPowderGrinder,
    /// Carving knife, an adventure mode decoration/weapon.
    #[token_de(token = "MEAT_CARVING")]
    MeatCarving,
    /// Boning knife, an adventure mode decoration/weapon.
    #[token_de(token = "MEAT_BONING")]
    MeatBoning,
    /// Slicing knife, an adventure mode decoration/weapon.
    #[token_de(token = "MEAT_SLICING")]
    MeatSlicing,
    /// Meat cleaver, an adventure mode decoration/weapon.
    #[token_de(token = "MEAT_CLEAVING")]
    MeatCleaving,
    /// Carving fork, an adventure mode decoration/weapon.
    #[token_de(token = "HOLD_MEAT_FOR_CARVING")]
    HoldMeatForCarving,
    /// Bowl, an adventure mode decoration/weapon.
    #[token_de(token = "MEAL_CONTAINER")]
    MealContainer,
    /// Nest box for your birds to lay eggs.
    #[token_de(token = "NEST_BOX")]
    NestBox,
    /// Jug; can store honey or oil.
    #[token_de(token = "LIQUID_CONTAINER")]
    LiquidContainer,
    /// Large pot; can store beer.
    #[token_de(token = "FOOD_STORAGE")]
    FoodStorage,
    /// Hive; can make honey.
    #[token_de(token = "HIVE")]
    Hive,
    /// Pouch, an adventure mode coin purse.
    #[token_de(token = "SMALL_OBJECT_STORAGE")]
    SmallObjectStorage,
    /// Minecart; item hauling/weapon.
    #[token_de(token = "TRACK_CART")]
    TrackCart,
    /// Wheelbarrow; allows hauling items faster.
    #[token_de(token = "HEAVY_OBJECT_HAULING")]
    HeavyObjectHauling,
    /// Stepladder, allows gathering fruit from trees.
    #[token_de(token = "STAND_AND_WORK_ABOVE")]
    StandAndWorkAbove,
    /// Scroll rollers.
    #[token_de(token = "ROLL_UP_SHEET")]
    RollUpSheet,
    /// Book binding.
    #[token_de(token = "PROTECT_FOLDED_SHEETS")]
    ProtectFoldedSheets,
    /// Scroll & quire.
    #[token_de(token = "CONTAIN_WRITING")]
    ContainWriting,
    /// Bookcase.
    #[token_de(token = "BOOKCASE")]
    Bookcase,
    /// Pedestal & display case for museums.
    #[token_de(token = "DISPLAY_OBJECT")]
    DisplayObject,
    /// Altar for (eventually) offering sacrifices.
    #[token_de(token = "PLACE_OFFERING")]
    PlaceOffering,
    /// Dice.
    #[token_de(token = "DIVINATION")]
    Divination,
    /// Dice.
    #[token_de(token = "GAMES_OF_CHANCE")]
    GamesOfChance,
}
impl Default for ToolUseEnum {
    fn default() -> Self {
        Self::LiquidCooking
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub enum ImprovementTypeWithMatFlagTokenArg {
    // TODO: token should be marked as deprecated/unused/ignored by the game, see #83
    // #[token_de(token = "ART_IMAGE")]
    // ArtImage,
    // #[token_de(token = "COVERED")]
    Covered(ImprovementMaterialFlagEnum),
    // #[token_de(token = "RINGS_HANGING")]
    RingsHanging(ImprovementMaterialFlagEnum),
    // #[token_de(token = "BANDS")]
    Bands(ImprovementMaterialFlagEnum),
    // #[token_de(token = "SPIKES")]
    Spikes(ImprovementMaterialFlagEnum),
    // #[token_de(token = "SPECIFIC", alias = "ITEMSPECIFIC")]
    Specific((ItemSpecificEnum, ImprovementMaterialFlagEnum)),
    // TODO: token should be marked as deprecated/unused/ignored by the game, see #83
    // #[token_de(token = "THREAD")]
    // Thread,
    // TODO: token should be marked as deprecated/unused/ignored by the game, see #83
    // #[token_de(token = "CLOTH")]
    // Cloth,
    // TODO: token should be marked as deprecated/unused/ignored by the game, see #83
    // #[token_de(token = "SEWN_IMAGE")]
    // SewnImage,
    // #[token_de(token = "PAGES")]
    Pages(ImprovementMaterialFlagEnum),
    // TODO: token should be marked as deprecated/unused/ignored by the game, see #83
    // #[token_de(token = "ILLUSTRATION")]
    // Illustration,
    // #[token_de(token = "INSTRUMENT_PIECE")]
    InstrumentPiece((Reference, ImprovementMaterialFlagEnum)), // Ref to INSTRUMENT PIECE, distinct from ITEM_INSTRUMENT
    // #[token_de(token = "WRITING")]
    Writing(ImprovementMaterialFlagEnum),
    // TODO: token should be marked as deprecated/unused/ignored by the game, see #83
    // #[token_de(token = "IMAGE_SET")]
    // ImageSet,
}
impl Default for ImprovementTypeWithMatFlagTokenArg {
    fn default() -> Self {
        Self::Covered(ImprovementMaterialFlagEnum::default())
    }
}

// Deserialize a token with following pattern: `[REF:improvement_type_token_arg:...]`
df_ls_syntax_analysis::token_deserialize_unary_token!(ImprovementTypeWithMatFlagTokenArg);

impl TryFromArgumentGroup for ImprovementTypeWithMatFlagTokenArg {
    fn try_from_argument_group(
        token: &mut Token,
        source: &str,
        diagnostics: &mut DiagnosticsInfo,
        add_diagnostics_on_err: bool,
    ) -> Result<Self, ()> {
        // Safe first argument (is not token_name) for error case
        let arg0 = match token.get_current_arg() {
            Ok(arg) => Ok(arg.clone()),
            Err(err) => Err(err),
        };
        let reference_arg0 =
            Reference::try_from_argument_group(token, source, diagnostics, add_diagnostics_on_err)?;
        let bp_criteria = match reference_arg0.0.as_ref() {
            "COVERED" => {
                let covered = ImprovementMaterialFlagEnum::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                ImprovementTypeWithMatFlagTokenArg::Covered(covered)
            }
            "RINGS_HANGING" => {
                let rings_hanging = ImprovementMaterialFlagEnum::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                ImprovementTypeWithMatFlagTokenArg::RingsHanging(rings_hanging)
            }
            "BANDS" => {
                let bands = ImprovementMaterialFlagEnum::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                ImprovementTypeWithMatFlagTokenArg::Bands(bands)
            }
            "SPIKES" => {
                let spikes = ImprovementMaterialFlagEnum::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                ImprovementTypeWithMatFlagTokenArg::Spikes(spikes)
            }
            // TODO: add alias warning
            "SPECIFIC" | "ITEMSPECIFIC" => {
                let specific =
                    <(ItemSpecificEnum, ImprovementMaterialFlagEnum)>::try_from_argument_group(
                        token,
                        source,
                        diagnostics,
                        add_diagnostics_on_err,
                    )?;
                ImprovementTypeWithMatFlagTokenArg::Specific(specific)
            }
            "PAGES" => {
                let pages = ImprovementMaterialFlagEnum::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                ImprovementTypeWithMatFlagTokenArg::Pages(pages)
            }
            "INSTRUMENT_PIECE" => {
                let instrument_piece =
                    <(Reference, ImprovementMaterialFlagEnum)>::try_from_argument_group(
                        token,
                        source,
                        diagnostics,
                        add_diagnostics_on_err,
                    )?;
                ImprovementTypeWithMatFlagTokenArg::InstrumentPiece(instrument_piece)
            }
            "WRITING" => {
                let writing = ImprovementMaterialFlagEnum::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                ImprovementTypeWithMatFlagTokenArg::Writing(writing)
            }
            _ => {
                Self::diagnostics_wrong_enum_type(
                    &arg0?,
                    vec![
                        "COVERED",
                        "RINGS_HANGING",
                        "BANDS",
                        "SPIKES",
                        "SPECIFIC",
                        "ITEMSPECIFIC",
                        "PAGES",
                        "INSTRUMENT_PIECE",
                        "WRITING",
                    ],
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                );
                return Err(());
            }
        };
        Ok(bp_criteria)
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum ItemSpecificEnum {
    #[token_de(token = "ROLLERS")]
    Rollers,
    #[token_de(token = "HANDLE")]
    Handle,
}
impl Default for ItemSpecificEnum {
    fn default() -> Self {
        Self::Rollers
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum ImprovementMaterialFlagEnum {
    /// Permits the improvement to be made from any bone.
    #[token_de(token = "BONE_MAT")]
    BoneMat,
    /// Permits the improvement to be made from any ceramic material.
    #[token_de(token = "CERAMIC_MAT")]
    CeramicMat,
    /// Permits the improvement to be made from any glass.
    #[token_de(token = "GLASS_MAT")]
    GlassMat,
    /// Permits the improvement to be made from anything with the `[ITEMS_HARD]` token, such as wood, stone
    /// or metal.
    #[token_de(token = "HARD_MAT")]
    HardMat,
    /// Permits the improvement to be made from any leather.
    #[token_de(token = "LEATHER_MAT")]
    LeatherMat,
    /// Permits the improvement to be made from anything with the `[IS_METAL]` token.
    #[token_de(token = "METAL_MAT")]
    MetalMat,
    /// Permits the improvement to be made from any metal with the `[ITEMS_WEAPON]` token.
    #[token_de(token = "METAL_WEAPON_MAT")]
    MetalWeaponMat,
    /// Permits the improvement to be made from any "sheet" material, such as papyrus, paper, and
    /// parchment. May be connected to the `PAPER_SLURRY`/`PAPER_PLANT` reaction classes,
    /// but this is not verified.
    #[token_de(token = "SHEET_MAT")]
    SheetMat,
    /// Permits the improvement to be made from any shell.
    #[token_de(token = "SHELL_MAT")]
    ShellMat,
    /// Permits the improvement to be made from any silk.
    #[token_de(token = "SILK_MAT")]
    SilkMat,
    /// Permits the improvement to be made from any material with the `[ITEMS_SOFT]` token, such as leather
    /// or textiles.
    #[token_de(token = "SOFT_MAT")]
    SoftMat,
    /// Permits the improvement to be made from any stone. Presumably connected to the `[IS_STONE]` token.
    #[token_de(token = "STONE_MAT")]
    StoneMat,
    /// Permits the improvement to be made from any plant fiber, such as pig tails.
    #[token_de(token = "THREAD_PLANT_MAT")]
    ThreadPlantMat,
    /// Permits the improvement to be made from any wood.
    #[token_de(token = "WOOD_MAT")]
    WoodMat,
}
impl Default for ImprovementMaterialFlagEnum {
    fn default() -> Self {
        Self::BoneMat
    }
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct ToyToken {
    /// Argument 1 of `[ITEM_TOY:...]`
    #[token_de(token = "ITEM_TOY", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    /// What this item will be called in-game.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>,
    /// Presumably prevents the item from being made from cloth, silk, or leather, present on
    /// everything but puzzleboxes and drums. Appears to work backwards for strange moods.
    #[token_de(token = "HARD_MAT")]
    pub hard_mat: Option<()>,
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct TrapCompToken {
    /// Argument 1 of `[ITEM_TRAPCOMP:...]`
    #[token_de(token = "ITEM_TRAPCOMP", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    /// What this item will be called in-game.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>,
    /// How much material is needed to make the item. Is most important with bars.
    /// The number of bars needed is the value divided by three.
    #[token_de(token = "MATERIAL_SIZE")]
    pub material_size: Option<u32>, // TODO: required
    /// Appears before the name of the weapon's material. For example: "menacing steel spike".
    #[token_de(token = "ADJECTIVE")]
    pub adjective: Option<String>,
    /// How large the item is. Defaults to 100.
    #[token_de(token = "SIZE")]
    pub size: Option<u32>,
    /// Number of times it hits. Defaults to 1
    #[token_de(token = "HITS")]
    pub hits: Option<u32>,
    /// Weapon may be installed in a screw pump.
    #[token_de(token = "IS_SCREW")]
    pub is_screw: Option<()>,
    /// Weapon may be installed in a spike trap.
    #[token_de(token = "IS_SPIKE")]
    pub is_spike: Option<()>,
    /// Weapon may be made out of wood.
    #[token_de(token = "WOOD")]
    pub wood: Option<()>,
    /// Weapon may be made out of metal.
    #[token_de(token = "METAL")]
    pub metal: Option<()>,
    /// Sets the attack characteristics of the weapon
    #[token_de(token = "ATTACK")]
    pub attack: Option<ItemAttack>,
}

#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct WeaponToken {
    /// Argument 1 of `[ITEM_WEAPON:...]`
    #[token_de(token = "ITEM_WEAPON", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    /// Name of the weapon.
    #[token_de(token = "NAME")]
    pub name: Option<(String, String)>, // TODO: required
    /// Number of bar units needed for forging, as well as the amount gained from melting.
    #[token_de(token = "MATERIAL_SIZE")]
    pub material_size: Option<u32>, // TODO: Required
    /// Adjective of the weapon, e.g. the "large" in "large copper dagger".
    #[token_de(token = "ADJECTIVE")]
    pub adjective: Option<String>,
    /// Volume of weapon in mL or cubic cm. Defaults to 100.
    #[token_de(token = "SIZE")]
    pub size: Option<u32>,
    /// The amount of force used when firing projectiles - velocity is presumably determined by the
    /// projectile's mass. Defaults to 0.
    #[token_de(token = "SHOOT_FORCE")]
    pub shoot_force: Option<u32>,
    /// The maximum speed a fired projectile can have.
    #[token_de(token = "SHOOT_MAXVEL")]
    pub shoot_maxvel: Option<u32>,
    /// The skill to determine effectiveness in melee with this weapon. Defaults to `MACE`.
    ///
    /// Skill of `AXE` will allow it to be used to chop down trees. Skill of `MINER` will allow
    /// it to be used for mining.
    ///
    /// Outsider adventurers (or regular ones with no points in offensive skills) will receive a
    /// weapon with the `SPEAR` skill, selected at random if multiple ones have been modded in.
    /// All adventurers will also start with a weapon using the `DAGGER` skill, again selected at
    /// random if multiple such weapons exist.
    #[token_de(token = "SKILL")]
    pub skill: Option<SkillEnum>,
    /// Makes this a ranged weapon that uses the specified ammo. The specified skill determines
    /// accuracy in ranged combat.
    #[token_de(token = "RANGED")]
    pub ranged: Option<(SkillEnum, Reference)>, // `TODO` reference is ammo class
    /// Creatures under this size (in cm^3) must use the weapon two-handed. Defaults to 50000.
    #[token_de(token = "TWO_HANDED")]
    pub two_handed: Option<u32>,
    /// Minimum body size (in cm^3) to use the weapon at all (multigrasp required until `TWO_HANDED`
    /// value). Defaults to 40000.
    #[token_de(token = "MINIMUM_SIZE")]
    pub minimum_size: Option<u32>,
    /// Allows the weapon to be made at a craftsdwarf's workshop from a sharp (`[MAX_EDGE:10000]` or
    /// higher) stone (i.e. obsidian) plus a wood log.
    #[token_de(token = "CAN_STONE")]
    pub can_stone: Option<()>,
    /// Restricts this weapon to being made of wood.
    #[token_de(token = "TRAINING")]
    pub training: Option<()>,
    /// List of attacks this weapon can have.
    #[token_de(token = "ATTACK")]
    pub attack: Vec<WeaponAttack>,
}

// region: Attack definitions

/// Specifies the attack characteristics of this item.
/// - attack type: `BLUNT` or `EDGE`
/// - contact_area: value
/// - penetration_size: value
/// - verb2nd: string
/// - verb3rd: string
/// - noun: string or `NO_SUB`
/// - velocity_multiplier: value
#[derive(Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq)]
pub struct ItemAttack {
    /// Arguments of the `ATTACK` token
    #[token_de(token = "ATTACK")]
    pub attack: Option<(
        AttackTypeEnum,
        u32,
        u32,
        String,
        String,
        Choose<NoSubEnum, String>,
        u32,
    )>,
    /// Determines the length of time to prepare this attack and until one can perform this attack
    /// again. Values appear to be calculated in adventure mode ticks.
    #[token_de(token = "ATTACK_PREPARE_AND_RECOVER")]
    pub attack_prepare_and_recover: Option<(u32, u32)>,
}

/// Defines an attack on this weapon.
/// You can have many `ATTACK` tags and one will be randomly selected for each attack, with `EDGE`
/// attacks used 100 times more often than `BLUNT` attacks.
///
/// Contact area is usually high for slashing and low for bludgeoning, piercing, and poking.
/// Penetration value tends to be low for slashing and high for stabbing. Penetration is ignored if
/// attack is `BLUNT`.
/// - attack type: `BLUNT` or `EDGE`
/// - contact_area: value
/// - penetration_size: value
/// - verb2nd: string
/// - verb3rd: string
/// - noun: string or `NO_SUB`
/// - velocity_multiplier: value
#[derive(Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq)]
pub struct WeaponAttack {
    /// Arguments of the `ATTACK` token
    #[token_de(token = "ATTACK", on_duplicate_to_parent, primary_token)]
    pub attack: Option<(
        AttackTypeEnum,
        u32,
        u32,
        String,
        String,
        Choose<NoSubEnum, String>,
        u32,
    )>,
    /// Determines the length of time to prepare this attack and until one can perform this attack
    /// again. Values appear to be calculated in adventure mode ticks.
    #[token_de(token = "ATTACK_PREPARE_AND_RECOVER")]
    pub attack_prepare_and_recover: Option<(u32, u32)>,
    /// Multiple strikes with this attack cannot be performed effectively.
    #[token_de(token = "ATTACK_FLAG_BAD_MULTIATTACK")]
    pub attack_flag_bad_multiattack: Option<()>,
    /// Multiple strikes with this attack can be performed with no penalty.
    #[token_de(token = "ATTACK_FLAG_INDEPENDENT_MULTIATTACK")]
    pub attack_flag_independent_multiattack: Option<()>,
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum AttackTypeEnum {
    #[token_de(token = "EDGE")]
    Edge,
    #[token_de(token = "BLUNT")]
    Blunt,
}
impl Default for AttackTypeEnum {
    fn default() -> Self {
        Self::Edge
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum NoSubEnum {
    #[token_de(token = "NO_SUB")]
    NoSub,
}
impl Default for NoSubEnum {
    fn default() -> Self {
        Self::NoSub
    }
}
// endregion