df_ls_structure 0.3.0-rc.1

A language server for Dwarf Fortress RAW files
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
use crate::{
    BreathFlowEnum, BreathMaterialEnum, CasteFlagEnum, CreatureFlagEnum, CreatureToken,
    EffectLocationEnum, MaterialTokenArg, SphereEnum, SynTransmittionMethodEnum, SyndromeToken,
    TargetPropertyEnum,
};
use df_ls_core::{Choose, Reference, ReferenceTo, Referenceable};
use df_ls_diagnostics::DiagnosticsInfo;
use df_ls_syntax_analysis::{Token, TokenDeserialize, TryFromArgumentGroup};
use serde::{Deserialize, Serialize};

/// Define a new interaction.
#[derive(
    Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct InteractionToken {
    /// Argument 1 of `[INTERACTION:...]`
    #[token_de(token = "INTERACTION", on_duplicate_to_parent, primary_token)]
    #[referenceable(self_reference)]
    pub reference: Option<ReferenceTo<Self>>,
    /// This disallows use of the interaction in play, and also encourages usage specifically to
    /// create experimental populations.
    #[token_de(token = "EXPERIMENT_ONLY")]
    pub experiment_only: Option<()>,
    /// Defines what things are capable of triggering this interaction - multiple sources may be
    /// specified.
    #[token_de(token = "I_SOURCE")]
    pub i_source: Vec<ISource>,
    /// Defines the targets available for subsequent use with `[I_EFFECT]` tokens. Multiple targets
    /// may be specified; the precise target(s) used with each interaction effect are indicated via
    /// their ID as specified in `IE_TARGET`.
    #[token_de(token = "I_TARGET")]
    pub i_target: Vec<ITarget>, // ref here is an "ID" used in other parts of the interaction
    /// Specifies what the interaction does to the targets. Multiple `[I_EFFECT]`s may be specified in
    /// a single interaction, and the same type may be used more than once.
    #[token_de(token = "I_EFFECT")]
    pub i_effect: Vec<IEffect>,
    /// Indicates that this is a generated interaction. Cannot be specified in user-defined raws.
    #[token_de(token = "GENERATED")]
    pub generated: Option<()>,
}

#[derive(Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq)]
pub struct ISource {
    /// Argument 1 of `[I_SOURCE:...]`
    #[token_de(token = "I_SOURCE", on_duplicate_to_parent, primary_token)]
    pub reference: Option<SourceEnum>,
    /// Describes what the interaction did to a historical figure; this is displayed in legends mode
    /// following the name of the historical figure who performed the interaction and preceding the
    /// name of the targeted historical figure (or, in the case of `[I_SOURCE:INGESTION]`, the
    /// historical figure from whom the consumed material was extracted).
    ///
    /// You must include a space at the start of the string for the game to format it properly.
    ///
    /// Example: `[IS_HIST_STRING_1: cursed]`
    #[token_de(token = "IS_HIST_STRING_1")]
    pub is_hist_string_1: Option<String>,
    /// Describes what the interaction did to a historical figure; this is displayed in legends mode
    /// after the name of the historical figure who was targeted by the interaction. In the case of
    /// `[I_SOURCE:INGESTION]`, it is displayed after the name of the historical figure from whom
    /// the consumed material was extracted.
    ///
    /// You must include a space at the start of the string for the game to format it properly.
    ///
    /// Example: `[IS_HIST_STRING_2: to assume the form of a lizard-like monster every full moon]`
    #[token_de(token = "IS_HIST_STRING_2")]
    pub is_hist_string_2: Option<String>,
    /// Displayed as an announcement when the interaction is carried out during play. The text
    /// follows the name of the target unit, and is preceded by `IS_TRIGGER_STRING_SECOND` or
    /// `IS_TRIGGER_STRING_THIRD`.
    ///
    /// May be limited to `[I_SOURCE:DEITY]` and `[I_SOURCE:EXPERIMENT]` interactions at present;
    /// this still needs to be tested.
    ///
    /// You must include a space at the start of the string for the game to format it properly.
    ///
    /// Example: `[IS_TRIGGER_STRING: been infected with a contagious ghoulish condition]`
    #[token_de(token = "IS_TRIGGER_STRING")]
    pub is_trigger_string: Option<String>,
    /// Presented before the `IS_TRIGGER_STRING` when describing the event in the second person.
    ///
    /// You must include a space at the start of the string for the game to format it properly.
    ///
    /// Example: `[IS_TRIGGER_STRING_SECOND: have]`
    #[token_de(token = "IS_TRIGGER_STRING_SECOND")]
    pub is_trigger_string_second: Option<String>,
    /// Presented before the `IS_TRIGGER_STRING` when describing the event in the third person.
    ///
    /// You must include a space at the start of the string for the game to format it properly.
    ///
    /// Example: `[IS_TRIGGER_STRING_THIRD: has]`
    #[token_de(token = "IS_TRIGGER_STRING_THIRD")]
    pub is_trigger_string_third: Option<String>,
    /// Generally used with `[I_SOURCE:SECRET]` interactions to describe what the secret is about
    /// (though it may be used to name any `I_SOURCE`). This name is engraved onto the appropriate
    /// secret-containing slabs from worldgen, and is used in legends mode when describing the
    /// secret being learnt by historical figures.
    ///
    /// Example: `[IS_NAME:the secrets of life and death]`
    #[token_de(token = "IS_NAME")]
    pub is_name: Option<String>,
    /// Indicates the sphere to which this secret pertains. Only one sphere can be defined for each
    /// `[I_SOURCE:SECRET]` token, so several `[I_SOURCE:SECRET]` tokens are required to make a
    /// secret belong to multiple spheres.
    #[token_de(token = "IS_SPHERE")]
    pub is_sphere: Option<SphereEnum>,
    /// Indicates why somebody would want to learn the secret. It has many allowed values currently,
    /// but only `IMMORTALITY` will result in a secret being pursued during world-gen.
    #[token_de(token = "IS_SECRET_GOAL")]
    pub is_secret_goal: Vec<SecretGoalEnum>,
    /// Indicates how the secret can be learned.
    #[token_de(token = "IS_SECRET")]
    pub is_secret: Vec<(SecretLearnMethodEnum, Option<(String, String)>)>, // TODO: the strings are filepaths, add a new type for filepaths
    /// Indicates why a deity would choose to perform this interaction.
    #[token_de(token = "IS_USAGE_HINT")]
    pub is_usage_hint: Option<LimitedUsageHintEnum>,
    /// Indicates what types of regions are capable of performing this interaction. This token may
    /// be specified several times per `I_SOURCE` to permit multiple terrain/alignment types.
    #[token_de(token = "IS_REGION")]
    pub is_region: Vec<RegionTypeEnum>,
    /// When used with `[I_SOURCE:REGION]`, determines how likely it is for the region(s) specified
    /// via `[IS_REGION]` to possess this interaction.
    ///
    /// Note: it appears that regions aren't allowed to possess more than a single regional
    /// interaction at present.
    #[token_de(token = "IS_FREQUENCY")]
    pub is_frequency: Option<i32>,
}

#[derive(Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq)]
pub struct ITarget {
    /// Arguments of `[I_TARGET:...]`
    #[token_de(token = "I_TARGET", on_duplicate_to_parent, primary_token)]
    pub reference: Option<(Reference, TargetTypeEnum)>,
    /// This is often included after `[I_TARGET]` token to add more detail about the target.
    #[token_de(token = "IT_LOCATION")]
    pub it_location: Option<TargetLocationEnum>,
    /// Tells the adventure mode player what they should be selecting. If not specified, the player
    /// will only be able to target themselves.
    #[token_de(token = "IT_MANUAL_INPUT")]
    pub it_manual_input: Option<String>,
    /// Specifies specific creatures the interaction can target.
    #[token_de(token = "IT_AFFECTED_CREATURE")]
    pub it_affected_creature: Vec<(ReferenceTo<CreatureToken>, Reference)>, // ref here is creature caste
    /// Specifies creature classes the interaction can target.
    #[token_de(token = "IT_AFFECTED_CLASS")]
    pub it_affected_class: Vec<Reference>,
    /// Specifies specific creatures the interaction cannot target.
    #[token_de(token = "IT_IMMUNE_CREATURE")]
    pub it_immune_creature: Vec<(ReferenceTo<CreatureToken>, Reference)>, // ref here is creature caste
    /// Specifies creature classes the interaction cannot target.
    #[token_de(token = "IT_IMMUNE_CLASS")]
    pub it_immune_class: Vec<Reference>,
    /// Indicates that the target must have the specified property.
    #[token_de(token = "IT_REQUIRES")]
    pub it_requires: Vec<(Vec<TargetPropertyEnum>,)>,
    /// Indicates that the target must not have the specified property.
    #[token_de(token = "IT_FORBIDDEN")]
    pub it_forbidden: Vec<(Vec<TargetPropertyEnum>,)>,
    /// Prevents the interaction from targeting a creature that's already under the effect of the
    /// same interaction.
    #[token_de(token = "IT_CANNOT_TARGET_IF_ALREADY_AFFECTED")]
    pub it_cannot_target_if_already_affected: Option<()>,
    /// Prevents the interaction from targeting a creature under the effects of a syndrome having
    /// the specified `SYN_CLASS` value.
    #[token_de(token = "IT_CANNOT_HAVE_SYNDROME_CLASS")]
    pub it_cannot_have_syndrome_class: Vec<Reference>,
    /// Specifies the type of material the interaction targets; currently only used for
    /// `MATERIAL_EMISSION` interaction effects.
    #[token_de(token = "IT_MATERIAL")]
    pub it_material: Option<InteractionMaterialEmissionTypeTokenArg>,
}

#[derive(Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq)]
pub struct IEffect {
    /// Argument 1 of `[I_EFFECT:...]`
    #[token_de(token = "I_EFFECT", on_duplicate_to_parent, primary_token)]
    pub reference: Option<EffectEnum>, // TODO semantic: restrict allowed tokens based on effect
    /// List of syndromes added by the interaction.
    #[token_de(token = "SYNDROME")]
    pub syndrome: Vec<SyndromeToken>,
    /// Allows the interaction effect to be applied directly to newly spawned creatures in arena
    /// mode. The specified name is used to represent it within the creature creation effects list.
    #[token_de(token = "IE_ARENA_NAME")]
    pub ie_arena_name: Option<String>,
    /// Specifies which `I_TARGET` a particular interaction effect will be applied to. For example,
    /// in an interaction with the token `[I_TARGET:B:CREATURE]`, 'B' is the `ID` used to indicate
    /// this target option. `[I_EFFECT:ADD_SYNDROME]` followed by `[IE_TARGET:B]` would therefore
    /// apply the syndrome to this target. Certain types of interaction effects require multiple
    /// `IE_TARGET` tokens in a specific order to function properly. A few effects do not require
    /// a target at all.
    #[token_de(token = "IE_TARGET")]
    pub ie_target: Vec<Reference>, // TODO some kind of semantic stuff using this I think
    /// Only appears to work with `[I_SOURCE:REGION]` interactions. Indicates that the effect
    /// happens intermittently and specifies roughly how often. Regional interactions aren't able to
    /// use effects which lack this token.
    ///
    /// `WEEKLY` is the only value that seems to work right now, despite `DAILY`, `MONTHLY` and
    /// `YEARLY` also existing in the string dump.
    #[token_de(token = "IE_INTERMITTENT")]
    pub ie_intermittent: Option<IntermittentFrequencyEnum>,
    /// Indicates that the effect happens immediately.
    #[token_de(token = "IE_IMMEDIATE")]
    pub ie_immediate: Option<()>,
    /// Prevents the interaction effect from manifesting unless the target is in a location which
    /// meets the specified criteria.
    ///
    /// Note: `NO_THICK_FOG` and `OUTSIDE` are accepted as valid location hints when specified with
    /// `IE_LOCATION`, but don't appear to work. It's possible that they're currently only
    /// implemented for use with `[CDI:LOCATION_HINT]`.
    #[token_de(token = "IE_LOCATION")]
    pub ie_location: Option<EffectLocationEnum>,
    /// Indicates what type of weather is added.
    #[token_de(token = "IE_ADD_WEATHER")]
    pub ie_add_weather: Option<WeatherEnum>,
    /// Indicates what type of weather is removed.
    #[token_de(token = "IE_REMOVE_WEATHER")]
    pub ie_remove_weather: Option<WeatherEnum>,
    /// `[IE_GRIME_LEVEL:2]` appears in the default cleaning interaction, and may indicate amount of
    /// grime cleaned, but this isn't clear.
    #[token_de(token = "IE_GRIME_LEVEL")]
    pub ie_grime_level: Option<u32>,
    /// When a creature cleans off a contaminant which is associated with a syndrome, the syndrome
    /// will be contracted if it has a matching trigger flag. This is what enables cats to become
    /// slightly inebriated when licking off alcohol. `SYN_INGESTED` appears to be the only syndrome
    /// trigger flag that works in this context.
    #[token_de(token = "IE_SYNDROME_TAG")]
    pub ie_syndrome_tag: Option<SynTransmittionMethodEnum>,
    /// Indicates the amount of force that the target will be propelled with.
    #[token_de(token = "IE_PROPEL_FORCE")]
    pub ie_propel_force: Option<u32>,
    /// Defines what item will be created.
    #[token_de(token = "IE_ITEM")]
    pub ie_item: Option<(u8, u16, Reference, MaterialTokenArg)>, // ref here is an item token
    /// Defines what quality the created item shall have. Can either be specified in the form of a
    /// single, fixed quality (it seems that `ARTIFACT` can only be used in this manner), or a
    /// minimum and maximum level (in which case the quality is selected randomly).
    ///
    /// Valid values (numerals only except for `ARTIFACT`) are:
    /// - 0 = ordinary
    /// - 1 = well-crafted
    /// - 2 = finely crafted
    /// - 3 = superior quality
    /// - 4 = exceptional
    /// - 5 = masterwork
    /// - `ARTIFACT`
    #[token_de(token = "IE_ITEM_QUALITY")]
    pub ie_item_quality: Option<Choose<u8, ArtifactEnum>>,
    /// Defines a fixed quality level which the affected item(s) will be set to (decreasing or
    /// increasing in quality as necessary).
    ///
    /// Valid values are:
    /// - 0 = ordinary
    /// - 1 = well-crafted
    /// - 2 = finely crafted
    /// - 3 = superior quality
    /// - 4 = exceptional
    /// - 5 = masterwork
    #[token_de(token = "IE_SET_QUALITY")]
    pub ie_set_quality: Option<u8>,
    /// Determines how much the quality of the item(s) will be changed. For instance, improving a
    /// well-crafted `-item-` (quality level 1) by 2 will turn it into a superior-quality `*item*`
    /// (quality level 3). A negative value can be used to decrease quality. Quality cannot be
    /// increased beyond level 5 (masterwork) or decreased below level 0 (ordinary).
    #[token_de(token = "IE_CHANGE_QUALITY")]
    pub ie_change_quality: Option<i8>,
    /// Indicates which specific creature and caste will be created when using this interaction.
    /// `ANY` can be used in place of a specific caste token. Only one `[CREATURE]` may currently
    /// be specified per interaction effect.
    #[token_de(token = "CREATURE")]
    pub creature: Option<(ReferenceTo<CreatureToken>, Reference)>, // this ref is `ANY`, `ALL`, or a caste for the creature
    /// When this token is added to a random creature summoning effect, it narrows down the
    /// selection to creatures which have the specified creature flag. This token may be used
    /// multiple times per interaction effect; creatures which lack any of the indicated flags will
    /// never be summoned.
    #[token_de(token = "IE_CREATURE_FLAG")]
    pub ie_creature_flag: Vec<CreatureFlagEnum>,
    /// When this token is added to a random creature summoning effect, any creature with the
    /// specified creature flag will be excluded from the selection. This token may be used multiple
    /// times per interaction effect; creatures which possess any of the indicated flags will never
    /// be summoned.
    #[token_de(token = "IE_FORBIDDEN_CREATURE_FLAG")]
    pub ie_forbidden_creature_flag: Vec<CreatureFlagEnum>,
    /// When this token is added to a random creature summoning effect, it narrows down the
    /// selection to creatures which have the specified caste flag. This token may be used multiple
    /// times per interaction effect; creatures which lack any of the indicated flags will never be
    /// summoned.
    #[token_de(token = "IE_CREATURE_CASTE_FLAG")]
    pub ie_creature_caste_flag: Vec<CasteFlagEnum>,
    /// When this token is added to a random creature summoning effect, it excludes any creature
    /// with the specified caste flag. This token may be used multiple times per interaction effect;
    /// creatures which possess any of the indicated flags will never be summoned.
    #[token_de(token = "IE_FORBIDDEN_CREATURE_CASTE_FLAG")]
    pub ie_forbidden_creature_caste_flag: Vec<CasteFlagEnum>,
    /// When this token is added to a random creature summoning effect, it narrows down the
    /// selection to creatures which have at least one gait with an `<energy expenditure>` of 0 and a
    /// `<max speed>` less than or equal to the specified `<minimum gait speed>` ("less than" because
    /// lower is faster in the scale used for gait speed).
    #[token_de(token = "IE_HAVE_FAST_EFFORTLESS_GAIT_SPEED")]
    pub ie_have_fast_effortless_gait_speed: Option<u32>,
    /// When this token is added to a random creature summoning effect, it excludes any creatures
    /// which have at least one gait with an `<energy expenditure>` of 0 and a `<max speed>` value less
    /// than or equal to the specified `<maximum gait speed>` (note that larger values are slower in
    /// the scale used for gait speed).
    #[token_de(token = "IE_ALL_SLOW_EFFORTLESS_GAIT_SPEED")]
    pub ie_all_slow_effortless_gait_speed: Option<u32>,
    /// The summoned unit vanishes in a puff of smoke once a certain amount of time has elapsed. The
    /// time limit is a randomly selected number of ticks within the specified minimum-maximum time
    /// range. The unit will persist indefinitely if this token is omitted.
    #[token_de(token = "IE_TIME_RANGE")]
    pub ie_time_range: Option<(u32, u32)>,
    /// Makes the summoned unit behave as a pet of the unit who performed the summoning interaction.
    #[token_de(token = "IE_MAKE_PET_IF_POSSIBLE")]
    pub ie_make_pet_if_possible: Option<()>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub enum InteractionMaterialEmissionTypeTokenArg {
    /// Indicates the emission details should be obtained from `[CDI:MATERIAL]` or `[CDI:FLOW]`.
    ContextMaterial,
    /// The emission will consist of the specified special flow type.
    Flow(BreathFlowEnum),
    /// The emission will consist of the specified material dispersed in the specified manner.
    Material((MaterialTokenArg, BreathMaterialEnum)),
}
impl Default for InteractionMaterialEmissionTypeTokenArg {
    fn default() -> Self {
        Self::ContextMaterial
    }
}

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

impl TryFromArgumentGroup for InteractionMaterialEmissionTypeTokenArg {
    fn try_from_argument_group(
        token: &mut Token,
        source: &str,
        diagnostics: &mut DiagnosticsInfo,
        add_diagnostics_on_err: bool,
    ) -> Result<Self, ()> {
        let reference_arg0 =
            Reference::try_from_argument_group(token, source, diagnostics, add_diagnostics_on_err)?;
        let cdi_type = match reference_arg0.0.as_ref() {
            "CONTEXT_MATERIAL" => InteractionMaterialEmissionTypeTokenArg::ContextMaterial,
            "FLOW" => {
                let flow = BreathFlowEnum::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                InteractionMaterialEmissionTypeTokenArg::Flow(flow)
            }
            "MATERIAL" => {
                let material = <(MaterialTokenArg, BreathMaterialEnum)>::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                InteractionMaterialEmissionTypeTokenArg::Material(material)
            }
            _ => {
                return Err(());
            }
        };
        Ok(cdi_type)
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum SourceEnum {
    /// Specifies that the interaction may be used in conjunction with `[CAN_DO_INTERACTION]` and
    /// `[CE_CAN_DO_INTERACTION]`, but it isn't actually necessary for this. It might exist simply
    /// to allow for the inclusion of `IS_` tokens (detailed below) to be applied when the
    /// interaction is used in this context.
    #[token_de(token = "CREATURE_ACTION")]
    CreatureAction,
    /// Allows the interaction to be used in conjunction with `[SPECIALATTACK_INTERACTION]` and
    /// `[CE_SPECIAL_ATTACK_INTERACTION]`.
    #[token_de(token = "ATTACK")]
    Attack,
    /// Allows the interaction to be used in conjunction with `[CE_BODY_MAT_INTERACTION]`.
    #[token_de(token = "INGESTION")]
    Ingestion,
    /// Allows the interaction to be inflicted upon mortals by the gods, for reasons dictated by
    /// `[IS_USAGE_HINT]`.
    #[token_de(token = "DEITY")]
    Deity,
    /// Allows the interaction to act as a secret which can be learnt and passed on to others, as
    /// specified via `[IS_SECRET]`. Appropriate interaction effects with a creature target will be
    /// applied to individuals who learn the secret. It is possible to set restrictions on who may
    /// learn the secret by using creature target tokens as described below. Also see
    /// `[IS_SECRET_GOAL]` and `[IS_SPHERE]`.
    #[token_de(token = "SECRET")]
    Secret,
    /// Allows the interaction to take place spontaneously in regions specified using `[IS_REGION]`.
    /// Also see `[IS_FREQUENCY]` and `[IE_INTERMITTENT]`.
    #[token_de(token = "REGION")]
    Region,
    /// Allows the interaction to take place spontaneously in disturbed tombs
    #[token_de(token = "DISTURBANCE")]
    Disturbance,
    /// Allows the interaction to take place spontaneously in curious underground structures (which
    /// have since been removed in `v0.40.01`)
    #[token_de(token = "UNDERGROUND_SPECIAL")]
    UndergroundSpecial,
    /// Allows the interaction to be used when experimenting on creatures.
    #[token_de(token = "EXPERIMENT")]
    Experiment,
}
impl Default for SourceEnum {
    fn default() -> Self {
        Self::CreatureAction
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum SecretGoalEnum {
    /// Ingame description: Unknown; if you know the ingame description, please open an issue on the
    /// issue tracker, or tell us on the Discord server.
    ///
    /// Gameplay effects: Unknown, possibly none.
    #[token_de(token = "STAY_ALIVE")]
    StayAlive,
    /// Ingame description: Unknown; if you know the ingame description, please open an issue on the
    /// issue tracker, or tell us on the Discord server.
    ///
    /// Gameplay effects: Unknown, possibly none.
    #[token_de(token = "MAINTAIN_ENTITY_STATUS")]
    MaintainEntityStatus,
    /// Ingame description: "dreams of raising a family"
    ///
    /// Gameplay effects: Goal completed upon giving birth or fathering an infant.
    #[token_de(token = "START_A_FAMILY")]
    StartAFamily,
    /// Ingame description: "dreams of ruling the world"
    ///
    /// Gameplay effects: Unknown, possibly none.
    #[token_de(token = "RULE_THE_WORLD")]
    RuleTheWorld,
    /// Ingame description: "dreams of creating a great work of art"
    ///
    /// Gameplay effects: Goal completed upon creation of Artifact or Masterpiece.
    #[token_de(token = "CREATE_A_GREAT_WORK_OF_ART")]
    CreateAGreatWorkOfArt,
    /// Ingame description: "dreams of crafting a masterwork someday "
    ///
    /// Gameplay effects: Goal completed upon creation of Artifact or Masterpiece.
    #[token_de(token = "CRAFT_A_MASTERWORK")]
    CraftAMasterwork,
    /// Ingame description: "dreams of bringing lasting peace to the world"
    ///
    /// Gameplay effects: Unknown, possibly none.
    #[token_de(token = "BRING_PEACE_TO_THE_WORLD")]
    BringPeaceToTheWorld,
    /// Ingame description: "dreams of becoming a legendary warrior"
    ///
    /// Gameplay effects: Unknown, possibly none.
    #[token_de(token = "BECOME_A_LEGENDARY_WARRIOR")]
    BecomeALegendaryWarrior,
    /// Ingame description: "dreams of mastering a skill"
    ///
    /// Gameplay effects: Goal completed upon reaching Legendary skill status.
    #[token_de(token = "MASTER_A_SKILL")]
    MasterASkill,
    /// Ingame description: "dreams of falling in love"
    ///
    /// Gameplay effects: Unknown, possibly none.
    #[token_de(token = "FALL_IN_LOVE")]
    FallInLove,
    /// Ingame description: "dreams of seeing the great natural places of the world"
    ///
    /// Gameplay effects: Unknown, possibly none.
    #[token_de(token = "SEE_THE_GREAT_NATURAL_SITES")]
    SeeTheGreatNaturalSites,
    /// Ingame description: "has become obsessed with his/her own mortality"
    ///
    /// Gameplay effects: Leads to [necromancy](https://dwarffortresswiki.org/index.php/Necromancer).
    #[token_de(token = "IMMORTALITY")]
    Immortality,
}
impl Default for SecretGoalEnum {
    fn default() -> Self {
        Self::StayAlive
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum SecretLearnMethodEnum {
    /// Gods may gift the secret to their worshippers. Secrets with `[IS_SPHERE]` specified may only
    /// be granted by gods who have at least one matching sphere.
    #[token_de(token = "SUPERNATURAL_LEARNING_POSSIBLE")]
    SupernaturalLearningPossible,
    /// The secret can be researched by mundane means. This doesn't do anything at present
    /// ([source](http://www.bay12forums.com/smf/index.php?topic=169696.msg8243222#msg8243222)).
    #[token_de(token = "MUNDANE_RESEARCH_POSSIBLE")]
    MundaneResearchPossible,
    /// The secret can be taught to apprentices
    #[token_de(token = "MUNDANE_TEACHING_POSSIBLE")]
    MundaneTeachingPossible,
    /// The secret can be written in books with the specified title. If this tag is present, a slab
    /// will be created upon learning the secret by supernatural means.
    #[token_de(token = "MUNDANE_RECORDING_POSSIBLE")]
    MundaneRecordingPossible,
}
impl Default for SecretLearnMethodEnum {
    fn default() -> Self {
        Self::SupernaturalLearningPossible
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum LimitedUsageHintEnum {
    /// 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 LimitedUsageHintEnum {
    fn default() -> Self {
        Self::MinorBlessing
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum RegionTypeEnum {
    #[token_de(token = "ANY")]
    Any,
    #[token_de(token = "DESERT")]
    Desert,
    #[token_de(token = "FOREST")]
    Forest,
    #[token_de(token = "GLACIER")]
    Glacier,
    #[token_de(token = "GRASSLAND")]
    Grassland,
    #[token_de(token = "HILLS")]
    Hills,
    #[token_de(token = "LAKE")]
    Lake,
    #[token_de(token = "MOUNTAINS")]
    Mountains,
    #[token_de(token = "OCEAN")]
    Ocean,
    #[token_de(token = "SWAMP")]
    Swamp,
    #[token_de(token = "TUNDRA")]
    Tundra,
    /// Permits the interaction to occur in all regions which meet alignment specifications.
    #[token_de(token = "ANY_TERRAIN")]
    AnyTerrain,
    #[token_de(token = "NORMAL_ALLOWED")]
    NormalAllowed,
    #[token_de(token = "EVIL_ALLOWED")]
    EvilAllowed,
    #[token_de(token = "GOOD_ALLOWED")]
    GoodAllowed,
    #[token_de(token = "SAVAGE_ALLOWED")]
    SavageAllowed,
    #[token_de(token = "EVIL_ONLY")]
    EvilOnly,
    #[token_de(token = "GOOD_ONLY")]
    GoodOnly,
    #[token_de(token = "SAVAGE_ONLY")]
    SavageOnly,
}
impl Default for RegionTypeEnum {
    fn default() -> Self {
        Self::Any
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum TargetTypeEnum {
    /// The target is a `CORPSE` or `CORPSEPIECE` item.
    #[token_de(token = "CORPSE")]
    Corpse,
    /// The target is a unit.
    #[token_de(token = "CREATURE")]
    Creature,
    /// This is a valid target for use in `[I_EFFECT:MATERIAL_EMISSION]` interaction effects, and is
    /// used to set the material or flow type of the emission. This information, in turn, is either
    /// obtained from an `[IT_MATERIAL]` token or by using `[IT_LOCATION:CONTEXT_MATERIAL]`. Using
    /// the latter implies that the precise emission info will be provided when defining how an
    /// interaction user can use the interaction in question via `CDI` tokens, enabling one to
    /// create 'template' material emission interactions such as the `MATERIAL_EMISSION` and
    /// `MATERIAL_EMISSION_WITH_HIDE` interactions included in the vanilla raws.
    #[token_de(token = "MATERIAL")]
    Material,
    /// The target is a local map tile. If used with `[IT_LOCATION:CONTEXT_CREATURE_OR_LOCATION]`,
    /// creatures at the target tile are also valid targets.
    #[token_de(token = "LOCATION")]
    Location,
}
impl Default for TargetTypeEnum {
    fn default() -> Self {
        Self::Corpse
    }
}

// TODO semantic analysis to limit which values are allowed based on the parent `I_TARGET`
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub enum TargetLocationEnum {
    /// Used with `CREATURE` to target the whole unit.
    ContextCreature,
    /// Used with `CREATURE` to target the body part specified in `[CDI:BP_REQUIRED]`.
    ContextBp,
    /// Used with `LOCATION` to target only a tile.
    ContextLocation,
    /// Used with `LOCATION` to allow for targeting of both creatures and tiles.
    ContextCreatureOrLocation,
    /// Used with `CORPSE`.
    ContextItem,
    /// Can only be used by `[I_SOURCE:REGION]` interactions.
    ContextRegion,
    /// Used with `MATERIAL` if you want an `[I_EFFECT:MATERIAL_EMISSION]` to obtain the emission
    /// material/flow type from `[CDI:MATERIAL]` or `[CDI:FLOW]`.
    ContextMaterial,
    /// Used with `LOCATION`. Targets a location from somewhere random within a number of squares
    /// from another `LOCATION` target specified by its target ID. For example,
    /// `[I_TARGET:B:LOCATION]` with `[IT_LOCATION:RANDOM_NEARBY_LOCATION:A:5]` will randomly select
    /// a tile lying somewhere within a radius of 5 tiles from `[I_TARGET:A:LOCATION]`. A walkable
    /// path between the two locations must exist.
    RandomNearbyLocation((Reference, i32)),
}
impl Default for TargetLocationEnum {
    fn default() -> Self {
        Self::ContextCreature
    }
}

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

impl TryFromArgumentGroup for TargetLocationEnum {
    fn try_from_argument_group(
        token: &mut Token,
        source: &str,
        diagnostics: &mut DiagnosticsInfo,
        add_diagnostics_on_err: bool,
    ) -> Result<Self, ()> {
        let reference_arg0 =
            Reference::try_from_argument_group(token, source, diagnostics, add_diagnostics_on_err)?;
        let cdi_type = match reference_arg0.0.as_ref() {
            "CONTEXT_CREATURE" => TargetLocationEnum::ContextCreature,
            "CONTEXT_BP" => TargetLocationEnum::ContextBp,
            "CONTEXT_LOCATION" => TargetLocationEnum::ContextLocation,
            "CONTEXT_CREATURE_OR_LOCATION" => TargetLocationEnum::ContextCreatureOrLocation,
            "CONTEXT_ITEM" => TargetLocationEnum::ContextItem,
            "CONTEXT_REGION" => TargetLocationEnum::ContextRegion,
            "CONTEXT_MATERIAL" => TargetLocationEnum::ContextMaterial,
            "RANDOM_NEARBY_LOCATION" => {
                let targetref_and_distance = <(Reference, i32)>::try_from_argument_group(
                    token,
                    source,
                    diagnostics,
                    add_diagnostics_on_err,
                )?;
                TargetLocationEnum::RandomNearbyLocation(targetref_and_distance)
            }
            _ => {
                return Err(());
            }
        };
        Ok(cdi_type)
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum EffectEnum {
    /// Adds one or more syndromes to a valid creature target. You must specify the syndrome details
    /// just below this interaction effect using the `[SYNDROME]` tag followed by the relevant
    /// syndrome tokens. See [here](https://dwarffortresswiki.org/index.php/Syndrome#The_anatomy_of_a_syndrome)
    /// for more information.
    #[token_de(token = "ADD_SYNDROME")]
    AddSyndrome,
    /// Raises the target corpse/bodypart as an undead unit. The zombie will always be hostile to
    /// life and will retain no information about its original personality/loyalties. Syndromes can
    /// also be specified within this tag. If a regional interaction contains this effect, affected
    /// regions will have undead wildlife.
    #[token_de(token = "ANIMATE")]
    Animate,
    /// Takes a target corpse and returns the creature to life. This can be used on parts that are
    /// not `FIT_FOR_RESURRECTION`, but only the main part (with an `UPPERBODY` attached) will
    /// remain loyal to its original faction. Syndromes can also be specified within this tag.
    #[token_de(token = "RESURRECT")]
    Resurrect,
    /// Removes contaminants from a valid creature target. See `[IE_GRIME_LEVEL]` and
    /// `[IE_SYNDROME_TAG]`.
    #[token_de(token = "CLEAN")]
    Clean,
    /// Causes the creatures to touch.
    #[token_de(token = "CONTACT")]
    Contact,
    /// Causes a particular material to be emitted. Used by evil weather and the `MATERIAL_EMISSION`
    /// interaction.
    #[token_de(token = "MATERIAL_EMISSION")]
    MaterialEmission,
    /// Allows the creature to hide even if another creature can see it.
    #[token_de(token = "HIDE")]
    Hide,
    /// Creates an item as described by `[IE_ITEM]` and `[IE_ITEM_QUALITY]`.
    #[token_de(token = "CREATE_ITEM")]
    CreateItem,
    /// Alters an item's quality level as indicated by either `[IE_CHANGE_QUALITY]` or
    /// `[IE_SET_QUALITY]`. When targeting a unit, all items equipped by that unit will be affected.
    #[token_de(token = "CHANGE_ITEM_QUALITY")]
    ChangeItemQuality,
    /// Creates a new unit at the target. The type of unit can either be specified using the
    /// `[CREATURE]` token, or made to be randomly selected as indicated by a variety of flag-based
    /// tokens: `[IE_CREATURE_FLAG]`, `[IE_FORBIDDEN_CREATURE_FLAG]`, `[IE_CREATURE_CASTE_FLAG]`,
    /// `[IE_FORBIDDEN_CREATURE_CASTE_FLAG]`, `[IE_HAVE_FAST_EFFORTLESS_GAIT_SPEED]` and/or
    /// `[IE_ALL_SLOW_EFFORTLESS_GAIT_SPEED]`. See also `[IE_TIME_RANGE]` and
    /// `[IE_MAKE_PET_IF_POSSIBLE]`.
    #[token_de(token = "SUMMON_UNIT")]
    SummonUnit,
    /// Applies a force specified using `[IE_PROPEL_FORCE]` to a unit to knock it back.
    #[token_de(token = "PROPEL_UNIT")]
    PropelUnit,
    /// Changes the weather as specified by `[IE_ADD_WEATHER]` and/or `[IE_REMOVE_WEATHER]`.
    #[token_de(token = "CHANGE_WEATHER")]
    ChangeWeather,
    /// Present in version `0.47.01` and accepted as a valid `I_EFFECT` token, but does not have an
    /// effect currently.
    #[token_de(token = "RAISE_GHOST")]
    RaiseGhost,
}
impl Default for EffectEnum {
    fn default() -> Self {
        Self::AddSyndrome
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum IntermittentFrequencyEnum {
    #[token_de(token = "WEEKLY")]
    Weekly,
    #[token_de(token = "DAILY")] // TODO mark as broken, see #83
    Daily,
    #[token_de(token = "MONTHLY")] // TODO mark as broken, see #83
    Monthly,
    #[token_de(token = "YEARLY")] // TODO mark as broken, see #83
    Yearly,
}
impl Default for IntermittentFrequencyEnum {
    fn default() -> Self {
        Self::Weekly
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum WeatherEnum {
    #[token_de(token = "FOG_MIST")]
    FogMist,
    #[token_de(token = "FOG_NORMAL")]
    FogNormal,
    #[token_de(token = "FOG_THICK")]
    FogThick,
    #[token_de(token = "FRONT_WARM")]
    FrontWarm,
    #[token_de(token = "FRONT_COLD")]
    FrontCold,
    #[token_de(token = "FRONT_OCCLUDED")]
    FrontOccluded,
    #[token_de(token = "STRATUS_ALTO")]
    StratusAlto,
    #[token_de(token = "STRATUS_PROPER")]
    StratusProper,
    #[token_de(token = "STRATUS_NIMBUS")]
    StratusNimbus,
    #[token_de(token = "CUMULUS_MED")]
    CumulusMed,
    #[token_de(token = "CUMULUS_MULTI")]
    CumulusMulti,
    #[token_de(token = "CUMULUS_NIMBUS")]
    CumulusNimbus,
    #[token_de(token = "CIRRUS")]
    Cirrus,
}
impl Default for WeatherEnum {
    fn default() -> Self {
        Self::FogMist
    }
}

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