df_ls_structure 0.3.0-rc.1

A language server for Dwarf Fortress RAW files
Documentation
use crate::{
    BpCriteriaTokenArg, BreathFlowEnum, BreathMaterialEnum, EffectLocationEnum, InteractionToken,
    MaterialTokenArgWithLocalCreatureMat, NotApplicableEnum,
};
use df_ls_core::{Choose, Reference, ReferenceTo};
use df_ls_diagnostics::DiagnosticsInfo;
use df_ls_syntax_analysis::{Token, TokenDeserialize, TryFromArgumentGroup};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum CdiTargetTypeEnum {
    /// The source needs to be able to see the target
    #[token_de(token = "LINE_OF_SIGHT")]
    LineOfSight,
    /// The source needs to be able to touch the target
    #[token_de(token = "TOUCHABLE")]
    Touchable,
    /// The target must be whoever disturbed the source (this is currently only relevant to mummies,
    /// allowing them to curse solely the unit who disturbed their resting place)
    #[token_de(token = "DISTURBER_ONLY")]
    DisturberOnly,
    /// The target can be the source
    #[token_de(token = "SELF_ALLOWED")]
    SelfAllowed,
    /// The target is required to be the source
    #[token_de(token = "SELF_ONLY")]
    SelfOnly,
}
impl Default for CdiTargetTypeEnum {
    fn default() -> Self {
        Self::LineOfSight
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum UsageHintEnum {
    /// Creatures will target 'friendly' creatures, usually at random.
    #[token_de(token = "GREETING")]
    Greeting,
    /// Targets enemy creatures in combat. If the interaction specifies `SELF_ONLY`, CPU-controlled
    /// creatures will never use it.
    #[token_de(token = "ATTACK")]
    Attack,
    /// Used in combat. Creature will target itself.
    #[token_de(token = "DEFEND")]
    Defend,
    /// Used when fleeing an enemy. Creature will target itself.
    #[token_de(token = "FLEEING")]
    Fleeing,
    /// Creature targets itself when its body is covered with contaminants.
    #[token_de(token = "CLEAN_SELF")]
    CleanSelf,
    /// As above, but targets other friendly units instead.
    #[token_de(token = "CLEAN_FRIEND")]
    CleanFriend,
    /// Used when a creature is expressing contempt (for example, to a murderer). This is used in
    /// the default spitting interaction, for example.
    #[token_de(token = "NEGATIVE_SOCIAL_RESPONSE")]
    NegativeSocialResponse,
    /// This is also used in the default spitting interaction, and is presumably used in a similar
    /// context.
    #[token_de(token = "TORMENT")]
    Torment,
    /// Used in divination dice blessings. Targets the roller.
    #[token_de(token = "MINOR_BLESSING")]
    MinorBlessing,
    /// Used in divination dice blessings. Targets the roller.
    #[token_de(token = "MEDIUM_BLESSING")]
    MediumBlessing,
    /// Used in divination dice curses. Targets the roller.
    #[token_de(token = "MINOR_CURSE")]
    MinorCurse,
    /// Used in divination dice curses. Targets the roller.
    #[token_de(token = "MEDIUM_CURSE")]
    MediumCurse,
    /// Used in disturbance and deity curses. Targets the tomb disturber/temple defiler.
    #[token_de(token = "MAJOR_CURSE")]
    MajorCurse,
}
impl Default for UsageHintEnum {
    fn default() -> Self {
        Self::Greeting
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub enum CdiTokenArg {
    /// Specifies which interaction can be performed. This is only needed following
    /// `CE_CAN_DO_INTERACTION`; there's no need to include this after `CAN_DO_INTERACTION`
    /// as the latter allows you to specify the interaction directly.
    Interaction(ReferenceTo<InteractionToken>),
    /// Specifies the name of the interaction as it will appear on the adventure mode
    /// 'powers/abilities' menu.
    AdvName(String),
    /// Specifies how the creature chooses what to target. Target `ID` refers to an `I_TARGET`
    /// defined in the interaction itself. Multiple target types can be specified. If no target is
    /// specified, creature will target any available target within range, even through walls.
    Target((Reference, Vec<CdiTargetTypeEnum>)),
    /// Determines the maximum distance from the interaction user (in tiles) at which something
    /// can be considered a valid target. For `SOLID_GLOB`, `SHARP_ROCK`, `LIQUID_GLOB` and
    /// `FIREBALL` breath attacks, also determines how far the projectiles can fly before falling
    /// to the ground.
    TargetRange((Reference, u32)),
    /// Specifies the maximum number of things that can be selected for a particular `I_TARGET`.
    MaxTargetNumber(Choose<(Reference, u32), u32>),
    /// Prevents CPU-controlled creatures from using the interaction unless they are in a location
    /// which meets the specified criteria.
    LocationHint(EffectLocationEnum),
    /// Indicates when and how CPU-controlled creatures will use the interaction. If no hint is
    /// specified, the interaction will be used whenever a valid target is available.
    /// Multiple usage hints may be specified.
    UsageHint(UsageHintEnum),
    /// The creature must wait the specified number of ticks before being able to use the
    /// interaction again. The delay defaults to 20 ticks if this is omitted. Setting it to 0
    /// removes the delay altogether.
    ///
    /// Note: A `WAIT_PERIOD` of 10 is 10 ticks long in fortress mode, but only 1 tick long in
    /// adventurer mode.
    WaitPeriod(u32),
    /// Indicates that performing the interaction doesn't take any time.
    FreeAction,
    /// Indicates that a particular body part must be present in order to perform the interaction.
    /// Criteria are `BY_CATEGORY:category`, `BY_TYPE:type` (`GRASP`, for example), or `BY_TOKEN:token`
    /// (uses the body part `ID`).
    BpRequired(BpCriteriaTokenArg),
    /// Only creatures that can speak will be able to use the interaction. Might also be needed
    /// for `VERBAL_SPEECH`.
    Verbal,
    /// Specifies what the creature says when they perform the interaction. Filename path is
    /// relative to /data/speech.
    VerbalSpeech(String),
    /// Presumably allows two creatures with the same interaction to use it on each other
    /// simultaneously, for example, cats cleaning each other.
    CanBeMutual,
    /// When a creature uses the interaction, a combat report message will be displayed in the
    /// form:
    ///
    /// `[interaction user(s)] [VERB text] [target creature (if applicable)]`
    ///
    /// The 'self' text is presented when describing the interaction in the second person (that
    /// is, when the interaction is carried out by the player character in adventure mode), the
    /// 'other' text is presented when describing it in the third person, and the 'mutual' text is
    /// presented when the interaction is carried out in a mutual fashion.
    ///
    /// Examples of this token being used:
    /// - `[CDI:VERB:lick:licks:lick each other]`
    /// - `[CDI:VERB:gesture:gestures:NA]`
    Verb((String, String, Choose<NotApplicableEnum, String>)),
    /// **This token's purpose is unknown; if you know what it does,
    /// please open an issue on the issue tracker.**
    VerbReverse,
    /// When a creature uses the interaction, a message will display, describing the target as
    /// doing this.
    ///
    /// The first argument is the message when the effect is applied to onself, the
    /// second is when describing another creature using the interaction, for example:
    /// `[CDI:TARGET_VERB:shudder and begin to move:shudders and begins to move]`
    TargetVerb((String, String)), // TODO: the second one MIGHT possibly be `NotApplicableEnum`, haven't seen it anywhere
    /// Can be used with interactions that have `[I_EFFECT:MATERIAL_EMISSION]` and
    /// `[IT_LOCATION:CONTEXT_MATERIAL]` (or `[IT_MATERIAL:CONTEXT_MATERIAL]`) to make the
    /// interaction emit a special flow type.
    Flow(BreathFlowEnum),
    /// Can be used with interactions that have `[I_EFFECT:MATERIAL_EMISSION]` and
    /// `[IT_LOCATION:CONTEXT_MATERIAL]` (or `[IT_MATERIAL:CONTEXT_MATERIAL]`) to make the
    /// interaction emit the specified material in the manner described by the breath attack token
    /// used.
    Material((MaterialTokenArgWithLocalCreatureMat, BreathMaterialEnum)),
}
impl Default for CdiTokenArg {
    fn default() -> Self {
        Self::AdvName(String::default())
    }
}

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

impl TryFromArgumentGroup for CdiTokenArg {
    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 cdi_type = match reference_arg0.0.as_ref() {
            "INTERACTION" => {
                let interaction = ReferenceTo::<InteractionToken>::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::Interaction(interaction)
            }
            "ADV_NAME" => {
                let adv_name = String::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::AdvName(adv_name)
            }
            "TARGET" => {
                let target = <(Reference, Vec<CdiTargetTypeEnum>)>::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::Target(target)
            }
            "TARGET_RANGE" => {
                let target_range = <(Reference, u32)>::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::TargetRange(target_range)
            }
            "MAX_TARGET_NUMBER" => {
                let max_target_number = Choose::<(Reference, u32), u32>::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::MaxTargetNumber(max_target_number)
            }
            "LOCATION_HINT" => {
                let location_hint = EffectLocationEnum::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::LocationHint(location_hint)
            }
            "USAGE_HINT" => {
                let usage_hint = UsageHintEnum::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::UsageHint(usage_hint)
            }
            "WAIT_PERIOD" => {
                let wait_period = u32::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::WaitPeriod(wait_period)
            }
            "FREE_ACTION" => CdiTokenArg::FreeAction,
            "BP_REQUIRED" => {
                let bp_required = BpCriteriaTokenArg::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::BpRequired(bp_required)
            }
            "VERBAL" => CdiTokenArg::Verbal,
            "VERBAL_SPEECH" => {
                let verbal_speech = String::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::VerbalSpeech(verbal_speech)
            }
            "CAN_BE_MUTUAL" => CdiTokenArg::CanBeMutual,
            "VERB" => {
                let verb =
                    <(String, String, Choose<NotApplicableEnum, String>)>::try_from_argument_group(
                        token,
                        source,
                        diagnostics,
                        add_diagnostics_on_err,
                    )?;
                CdiTokenArg::Verb(verb)
            }
            "VERB_REVERSE" => CdiTokenArg::VerbReverse,
            "TARGET_VERB" => {
                let target_verb = <(String, String)>::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::TargetVerb(target_verb)
            }
            "FLOW" => {
                let flow = BreathFlowEnum::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::Flow(flow)
            }
            "MATERIAL" => {
                let material = <(MaterialTokenArgWithLocalCreatureMat, BreathMaterialEnum)>::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                CdiTokenArg::Material(material)
            }
            _ => {
                Self::diagnostics_wrong_enum_type(
                    &arg0?,
                    vec![
                        "INTERACTION",
                        "ADV_NAME",
                        "TARGET",
                        "TARGET_RANGE",
                        "MAX_TARGET_NUMBER",
                        "LOCATION_HINT",
                        "USAGE_HINT",
                        "WAIT_PERIOD",
                        "FREE_ACTION",
                        "BP_REQUIRED",
                        "VERBAL",
                        "VERBAL_SPEECH",
                        "CAN_BE_MUTUAL",
                        "VERB",
                        "VERB_REVERSE",
                        "TARGET_VERB",
                        "FLOW",
                        "MATERIAL",
                    ],
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                );
                return Err(());
            }
        };
        Ok(cdi_type)
    }
}