Skip to main content

shuyi_assets/dialogue/
present.rs

1//! Who is there when a scene plays. An `@if` block's steps play only for a
2//! character who is [`Present`]; [`Scene::resolved`] gives the scene as it
3//! plays for those present. Who that is, and which scripts must keep someone
4//! inside an `@if` block, is the game's to say.
5use crate::prelude::*;
6
7use alloc::collections::BTreeSet;
8
9use super::{CharacterId, ChoiceOption, Scene, Step};
10
11/// Who is there when a scene plays.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum Present {
14    /// Everyone: every `@if` block plays its first part (a scene viewer; a
15    /// scene played outside a story).
16    Everyone,
17    /// Only these characters.
18    Only(BTreeSet<CharacterId>),
19}
20
21impl Present {
22    /// Only `characters`.
23    pub fn only(characters: impl IntoIterator<Item = CharacterId>) -> Self {
24        Self::Only(characters.into_iter().collect())
25    }
26
27    /// Whether `character` is there.
28    pub fn has(&self, character: &CharacterId) -> bool {
29        match self {
30            Present::Everyone => true,
31            Present::Only(characters) => characters.contains(character),
32        }
33    }
34}
35
36impl<D: Clone> Scene<D> {
37    /// The scene as it plays for those `present`: every `@if` block
38    /// replaced by the steps of the part that holds, in reactions and in
39    /// other blocks too. The lines keep their ids.
40    #[must_use]
41    pub fn resolved(&self, present: &Present) -> Scene<D> {
42        Scene {
43            id: self.id.clone(),
44            steps: resolved(&self.steps, present),
45        }
46    }
47}
48
49impl<D> Scene<D> {
50    /// Whether the scene has a speech or narration line to play, whichever
51    /// reply is picked. Run on a [`Scene::resolved`] scene: one with
52    /// nothing left to say for those present isn't played.
53    pub fn has_text(&self) -> bool {
54        self.steps.iter().any(Step::has_text)
55    }
56}
57
58/// `steps` with every `@if` block replaced by the part that holds for
59/// those `present`.
60fn resolved<D: Clone>(steps: &[Step<D>], present: &Present) -> Vec<Step<D>> {
61    let mut out = Vec::with_capacity(steps.len());
62    for step in steps {
63        match step {
64            Step::If {
65                character,
66                then,
67                otherwise,
68            } => {
69                let part = if present.has(character) {
70                    then
71                } else {
72                    otherwise
73                };
74                out.extend(resolved(part, present));
75            }
76            Step::Choice { options } => {
77                let option = |o: &ChoiceOption<D>| ChoiceOption {
78                    steps: resolved(&o.steps, present),
79                    ..o.clone()
80                };
81                out.push(Step::Choice {
82                    options: options.iter().map(option).collect(),
83                });
84            }
85            other => out.push(other.clone()),
86        }
87    }
88    out
89}