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
use self::super::Leader;


/// Game's all possible states.
///
/// `Widgets::update()` takes care of proper state transformation.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum GameState {
    /// Display the main menu.
    ///
    /// Main menu contains a Start button, a button to Display Highscores, an Exit button.
    ///
    /// Can transform into:
    ///
    ///   * `ChooseDifficulty`
    ///   * `DisplayHighscores`
    ///   * `Exit`
    MainMenu,
    /// Display the screen where the player chooses the difficulty.
    ///
    /// The screen contains one button for each difficulty and the Back button.
    ///
    /// Can transform into:
    ///
    ///   * `Playing`
    ///   * `MainMenu`
    ChooseDifficulty,
    /// The game is currently in progress.
    ///
    /// This also contains the game's difficulty, the player's current score and whether the current fruit is a mango.
    ///
    /// Can transform into `GameOver`.
    Playing {
        /// The game difficulty, as chosen in the `ChooseDifficulty` stage.
        difficulty: Difficulty,
        /// The user's current score.
        score: u64,
        /// The current fruit index.
        ///
        /// If `None` - it's a mango, else it's an index of fruit from `util::FRUITS`.
        fruit: Option<usize>,
    },
    /// The game was lost after a valiant battle.
    ///
    /// Contains the game's difficulty, the player's final score and the Back button.
    ///
    /// In this stage the player enters its name.
    ///
    /// Can transform into:
    ///
    ///   * `GameEnded`
    ///   * `MainMenu`
    GameOver {
        /// The game difficulty, as same as in the `Playing` stage.
        difficulty: Difficulty,
        /// The user's final score.
        score: u64,
        /// The user's name, mostly partial.
        name: String,
    },
    /// The game cycle has ended. Semi-meta-state
    ///
    /// Contains the game's difficulty and the players final score.
    ///
    /// In this stage the player enters its name.
    ///
    /// Transforms into `MainMenu`.
    GameEnded {
        /// The user's name.
        name: String,
        /// User's final score, weighted.
        score: u64,
    },
    /// Meta-state indicating that the leaderboard needs to be loaded.
    ///
    /// Needs to be handled in usercode, place the leaderboard into `DisplayLeaderboard` after loading it.
    ///
    /// Leaderboards are loaded via `Leader::load()`.
    ///
    /// Transforms into `DisplayLeaderboard`
    LoadLeaderboard,
    /// Display top 10 high scores.
    ///
    /// This screen also contains the Back button.
    ///
    /// Can transform into `MainMenu`
    DisplayLeaderboard(Vec<Leader>),
    /// Pseudo-state, signifying that the game window should be closed.
    ///
    /// Can transform into: nothing. This is the final state all others seek.
    Exit,
}

/// The game's difficulty, chosen in the `ChooseDifficulty` step.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Difficulty {
    /// Easy difficulty, points are halved when sorting.
    Easy,
    /// Normal difficulty, points are untouched when sorting.
    Normal,
    /// Hard difficulty, points are doubled when sorting.
    Hard,
}

impl GameState {
    /// Check whether this state currently means that the game has ended.
    ///
    /// # Examples
    ///
    /// ```
    /// # use poke_a_mango::ops::GameState;
    /// assert!(GameState::Exit.should_exit());
    /// ```
    pub fn should_exit(&self) -> bool {
        *self == GameState::Exit
    }

    /// Check whether this state requires usercode to load the leaderboard.
    ///
    /// # Examples
    ///
    /// ```
    /// # use poke_a_mango::ops::GameState;
    /// assert!(GameState::LoadLeaderboard.should_load_leaderboard());
    /// ```
    pub fn should_load_leaderboard(&self) -> bool {
        *self == GameState::LoadLeaderboard
    }
}

impl Difficulty {
    /// The difficulty's numeric value, from `1` to `3`.
    pub fn numeric(&self) -> u32 {
        match *self {
            Difficulty::Easy => 1,
            Difficulty::Normal => 2,
            Difficulty::Hard => 3,
        }
    }

    /// Get the difficulty from its numeric value.
    pub fn from_numeric(num: u32) -> Option<Self> {
        match num {
            1 => Some(Difficulty::Easy),
            2 => Some(Difficulty::Normal),
            3 => Some(Difficulty::Hard),
            _ => None,
        }
    }

    /// How to much to multiply the player's points for sorting.
    ///
    /// Points on Easy are worth half, and on Hard - twice the normal amount.
    ///
    /// # Examples
    ///
    /// ```
    /// # use poke_a_mango::ops::Difficulty;
    /// // Normally you'd get these by playing
    /// let points = 20;
    /// let difficulty = Difficulty::Hard;
    ///
    /// let real_points = (points as f64) * difficulty.point_weight();
    /// assert_eq!(real_points, 40.0);
    /// ```
    pub fn point_weight(&self) -> f64 {
        match *self {
            Difficulty::Easy => 0.5,
            Difficulty::Normal => 1.0,
            Difficulty::Hard => 2.0,
        }
    }
}