Skip to main content

lf2_parse/element/
itr.rs

1use std::convert::TryFrom;
2
3use pest::iterators::Pair;
4
5use crate::{Error, FrameNumberNext, ObjectDataParser, Rule, SubRuleFn};
6
7pub use self::{
8    effect::{Effect, EffectParseError},
9    itr_kind::ItrKind,
10    itr_kind_parse_error::ItrKindParseError,
11};
12
13mod effect;
14mod itr_kind;
15mod itr_kind_parse_error;
16
17/// Area that hits other objects.
18///
19/// See https://lf-empire.de/lf2-empire/data-changing/frame-elements/174-itr-interaction?start=1
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub struct Itr {
22    /// Interaction variants.
23    pub kind: ItrKind,
24    /// X coordinate.
25    pub x: i32,
26    /// Y coordinate.
27    pub y: i32,
28    /// Width.
29    pub w: u32,
30    /// Height.
31    pub h: u32,
32    /// Z Width extends in both directions + 1 center pixel.
33    ///
34    /// `zwidth: 10` means 10 pixels up, 10 pixels down, and one pixel for
35    /// center of the shadow for 21 pixels total.
36    pub z_width: u32,
37    /// Acceleration to place on the hit object in the X axis.
38    ///
39    /// # Notes
40    ///
41    /// * `itr/kind:8` ([`ItrKind::HealBall`]) uses this as the frame number for
42    ///   the object with this `Itr` to switch to when hitting a character.
43    pub d_vx: i64,
44    /// Acceleration to place on the hit object in the Y axis.
45    pub d_vy: i64,
46    /// Delay before another hit may happen, restricts this `Itr` to one object.
47    pub a_rest: u32,
48    /// Delay before another hit may happen, allows multiple objects to be hit.
49    pub v_rest: u32,
50    /// How much a character is "off balance".
51    ///
52    /// The `fall` value determines how an attacked character will react to this
53    /// itr by flinching, getting into the stunned frames, or `falling`. If no
54    /// value is specified, the default of `20` will be used.
55    ///
56    /// * If a character accumulates `20` `fall` points, he switches to
57    ///   `injured1` (`220`).
58    /// * If a character accumulates `40` `fall` points, he switches to
59    ///   `injured2` (`222`) or `injured2back` (`224`) depending on the
60    ///   direction he is hit, and will fall if he is in mid-air.
61    /// * If a character accumulates `60` `fall` points, he switches into the
62    ///   `stunned` (`226`) frames where he can be grabbed or hit by
63    ///   `super_punch`.
64    ///
65    /// Attacks with `fall: 41` or more can hit `falling` characters.
66    ///
67    /// Here are a few values as a rule of thumb for various `fall` values:
68    ///
69    /// | `fall` | Description                                              |
70    /// | -----: | :------------------------------------------------------- |
71    /// |     -1 | Does not go into injured frames and harder to knockdown. |
72    /// |      1 | Never stun, never fall (Davis DvA shrafe)                |
73    /// |     20 | 3 hit stun, 4 hit fall                                   |
74    /// |     25 | 2 hit stun, 3 hit fall (Dennis normal kick)              |
75    /// |     40 | Does not stun, 2 hit fall (baseball bat normal swing)    |
76    /// |     60 | 1 hit stun, 2 hit fall (Henry's arrow)                   |
77    /// |     70 | 1 hit fall                                               |
78    ///
79    /// Every 1 TU, a `fall` point is deducted.
80    pub fall: i32,
81    /// Broken-defence points.
82    ///
83    /// `bdefend` points determine if a character is able to block an attack by
84    /// defending or if he will go to the broken-defense-frames. As long as he
85    /// has 30 or less Bdefend-points, he will be able to block the attack, if
86    /// it's 31 or higher, he goes to the broken-defense-frames. If an itr hits
87    /// the character while he is not in the defend-frames, his `bdefend`
88    /// counter will automatically increase to 45. If you have, for example,
89    /// accumulated 31 points and get hit during your defense (assuming you have
90    /// specified a positive `bdefend` value in the hitting itr), the character
91    /// will go to the broken-defense-frames.
92    ///
93    /// Here are some common values for various `bdefend` values:
94    ///
95    /// | `bdefend` | Description                                       |
96    /// | --------: | :------------------------------------------------ |
97    /// |         0 | never breaks defense (ex: John's D>J shield)      |
98    /// |        12 | 4 hit break                                       |
99    /// |        16 | 3 hit break                                       |
100    /// |        30 | 2 hit break                                       |
101    /// |        60 | 1 hit break                                       |
102    /// |       100 | ignores defense, sets `bdefend` counter to `45`,\
103    ///               and instantly destroys weapons.                   |
104    ///
105    /// Every 1 TU, a `bdefend` point is deducted, so he will be able to recover
106    /// his defense.
107    ///
108    /// Armor will function as long as a character has not accumulated more
109    /// `bdefend` points than the specific armor points of Louis(1), Knight or
110    /// Julian(15) at the time of attack. For example, Julian can resist a dash
111    /// attack(bdefend 60) even though he only has 15 armor points, but he will
112    /// be left completely vulnerable for the next 45 TU until he regains his
113    /// 1st armor point.
114    pub b_defend: i32,
115    /// Amount of damage to inflict on the target object.
116    ///
117    /// # Notes
118    ///
119    /// * `itr/kind:5` ([`ItrKind::WeaponStrength`]) ignores this and uses the
120    ///   `injury` in `weapon_strength_list`.
121    /// * `itr/kind:8` ([`ItrKind::HealBall`]) uses this for the number of HP to
122    ///   heal a character by.
123    pub injury: i32,
124    /// Itr `effect` variants.
125    pub effect: Effect,
126    /// Frame numbers for where the catching object should switch to.
127    ///
128    /// Used in `itr/kind: 1` ([`ItrKind::CatchStunned`]) and `itr/kind: 3`
129    /// ([`ItrKind::CatchForce`]).
130    pub catching_act: FrameNumberNext,
131    /// Frame numbers for where the caught character should switch to.
132    ///
133    /// Used in `itr/kind: 1` ([`ItrKind::CatchStunned`]) and `itr/kind: 3`
134    /// ([`ItrKind::CatchForce`]).
135    pub caught_act: FrameNumberNext,
136}
137
138impl Default for Itr {
139    fn default() -> Self {
140        Itr {
141            kind: Default::default(),
142            x: Default::default(),
143            y: Default::default(),
144            w: Default::default(),
145            h: Default::default(),
146            z_width: Self::Z_WIDTH_DEFAULT,
147            d_vx: Default::default(),
148            d_vy: Default::default(),
149            a_rest: Default::default(),
150            v_rest: Default::default(),
151            fall: Default::default(),
152            b_defend: Default::default(),
153            injury: Default::default(),
154            effect: Default::default(),
155            catching_act: Default::default(),
156            caught_act: Default::default(),
157        }
158    }
159}
160
161impl Itr {
162    /// Default `Z_WIDTH` for `Itr` volumes.
163    pub const Z_WIDTH_DEFAULT: u32 = 13;
164
165    fn parse_tags<'i>(itr: Itr, itr_data_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
166        itr_data_pair.into_inner().try_fold(itr, Itr::parse_tag)
167    }
168
169    fn parse_tag<'i>(itr: Itr, itr_tag_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
170        ObjectDataParser::parse_as_type(
171            itr,
172            itr_tag_pair,
173            Rule::ItrTag,
174            &[Self::parse_tag_value as SubRuleFn<_>],
175        )
176    }
177
178    fn parse_tag_value<'i>(mut itr: Itr, itr_tag_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
179        itr = match itr_tag_pair.as_rule() {
180            Rule::TagKind => {
181                ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_kind_value)?
182            }
183            Rule::TagX => ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_x_value)?,
184            Rule::TagY => ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_y_value)?,
185            Rule::TagW => ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_w_value)?,
186            Rule::TagH => ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_h_value)?,
187            Rule::TagZWidth => {
188                ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_z_width_value)?
189            }
190            Rule::TagDVx => {
191                ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_d_vx_value)?
192            }
193            Rule::TagDVy => {
194                ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_d_vy_value)?
195            }
196            Rule::TagARest => {
197                ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_a_rest_value)?
198            }
199            Rule::TagVRest => {
200                ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_v_rest_value)?
201            }
202            Rule::TagFall => {
203                ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_fall_value)?
204            }
205            Rule::TagBDefend => {
206                ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_b_defend_value)?
207            }
208            Rule::TagInjury => {
209                ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_injury_value)?
210            }
211            Rule::TagEffect => {
212                ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_effect_value)?
213            }
214            Rule::TagCatchingAct => {
215                ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_catching_act_value)?
216            }
217            Rule::TagCaughtAct => {
218                ObjectDataParser::parse_value(itr, itr_tag_pair, Self::parse_caught_act_value)?
219            }
220            _ => itr,
221        };
222        Ok(itr)
223    }
224
225    fn parse_kind_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
226        let kind = value_pair
227            .as_str()
228            .parse()
229            .map_err(|error| Error::ParseItrKind { value_pair, error })?;
230        itr.kind = kind;
231        Ok(itr)
232    }
233
234    fn parse_x_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
235        let x = value_pair
236            .as_str()
237            .parse()
238            .map_err(|error| Error::ParseInt {
239                field: stringify!(x),
240                value_pair,
241                error,
242            })?;
243        itr.x = x;
244        Ok(itr)
245    }
246
247    fn parse_y_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
248        let y = value_pair
249            .as_str()
250            .parse()
251            .map_err(|error| Error::ParseInt {
252                field: stringify!(y),
253                value_pair,
254                error,
255            })?;
256        itr.y = y;
257        Ok(itr)
258    }
259
260    fn parse_w_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
261        let w = value_pair
262            .as_str()
263            .parse()
264            .map_err(|error| Error::ParseInt {
265                field: stringify!(w),
266                value_pair,
267                error,
268            })?;
269        itr.w = w;
270        Ok(itr)
271    }
272
273    fn parse_h_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
274        let h = value_pair
275            .as_str()
276            .parse()
277            .map_err(|error| Error::ParseInt {
278                field: stringify!(h),
279                value_pair,
280                error,
281            })?;
282        itr.h = h;
283        Ok(itr)
284    }
285
286    fn parse_z_width_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
287        let z_width = value_pair
288            .as_str()
289            .parse()
290            .map_err(|error| Error::ParseInt {
291                field: stringify!(zwidth),
292                value_pair,
293                error,
294            })?;
295        itr.z_width = z_width;
296        Ok(itr)
297    }
298
299    fn parse_d_vx_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
300        let d_vx = value_pair
301            .as_str()
302            .parse()
303            .map_err(|error| Error::ParseInt {
304                field: stringify!(d_vx),
305                value_pair,
306                error,
307            })?;
308        itr.d_vx = d_vx;
309        Ok(itr)
310    }
311
312    fn parse_d_vy_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
313        let d_vy = value_pair
314            .as_str()
315            .parse()
316            .map_err(|error| Error::ParseInt {
317                field: stringify!(d_vy),
318                value_pair,
319                error,
320            })?;
321        itr.d_vy = d_vy;
322        Ok(itr)
323    }
324
325    fn parse_a_rest_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
326        let a_rest = value_pair
327            .as_str()
328            .parse()
329            .map_err(|error| Error::ParseInt {
330                field: stringify!(arest),
331                value_pair,
332                error,
333            })?;
334        itr.a_rest = a_rest;
335        Ok(itr)
336    }
337
338    fn parse_v_rest_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
339        let v_rest = value_pair
340            .as_str()
341            .parse()
342            .map_err(|error| Error::ParseInt {
343                field: stringify!(vrest),
344                value_pair,
345                error,
346            })?;
347        itr.v_rest = v_rest;
348        Ok(itr)
349    }
350
351    fn parse_fall_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
352        let fall = value_pair
353            .as_str()
354            .parse()
355            .map_err(|error| Error::ParseInt {
356                field: stringify!(fall),
357                value_pair,
358                error,
359            })?;
360        itr.fall = fall;
361        Ok(itr)
362    }
363
364    fn parse_b_defend_value<'i>(
365        mut itr: Itr,
366        value_pair: Pair<'i, Rule>,
367    ) -> Result<Itr, Error<'i>> {
368        let b_defend = value_pair
369            .as_str()
370            .parse()
371            .map_err(|error| Error::ParseInt {
372                field: stringify!(b_defend),
373                value_pair,
374                error,
375            })?;
376        itr.b_defend = b_defend;
377        Ok(itr)
378    }
379
380    fn parse_injury_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
381        let injury = value_pair
382            .as_str()
383            .parse()
384            .map_err(|error| Error::ParseInt {
385                field: stringify!(injury),
386                value_pair,
387                error,
388            })?;
389        itr.injury = injury;
390        Ok(itr)
391    }
392
393    fn parse_effect_value<'i>(mut itr: Itr, value_pair: Pair<'i, Rule>) -> Result<Itr, Error<'i>> {
394        let effect = value_pair
395            .as_str()
396            .parse()
397            .map_err(|error| Error::ParseItrEffect { value_pair, error })?;
398        itr.effect = effect;
399        Ok(itr)
400    }
401
402    fn parse_catching_act_value<'i>(
403        mut itr: Itr,
404        value_pair: Pair<'i, Rule>,
405    ) -> Result<Itr, Error<'i>> {
406        let catching_act = value_pair
407            .as_str()
408            .parse()
409            .map_err(|error| Error::ParseInt {
410                field: stringify!(catching_act),
411                value_pair,
412                error,
413            })?;
414        itr.catching_act = catching_act;
415        Ok(itr)
416    }
417
418    fn parse_caught_act_value<'i>(
419        mut itr: Itr,
420        value_pair: Pair<'i, Rule>,
421    ) -> Result<Itr, Error<'i>> {
422        let caught_act = value_pair
423            .as_str()
424            .parse()
425            .map_err(|error| Error::ParseInt {
426                field: stringify!(caught_act),
427                value_pair,
428                error,
429            })?;
430        itr.caught_act = caught_act;
431        Ok(itr)
432    }
433}
434
435impl<'i> TryFrom<Pair<'i, Rule>> for Itr {
436    type Error = Error<'i>;
437
438    fn try_from(pair: Pair<'i, Rule>) -> Result<Self, Self::Error> {
439        let sub_rule_fns: &[SubRuleFn<_>] = &[Itr::parse_tags];
440        ObjectDataParser::parse_as_type(Itr::default(), pair, Rule::Itr, sub_rule_fns)
441    }
442}