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
use crate::api::dungeon_mode::entity::DungeonEntity;
use crate::api::overlay::{CreatableWithLease, OverlayLoadLease};
use crate::api::random::{rand_u16_internal, Rng};
use crate::ctypes::c_int;
use crate::ffi;
use core::hint::unreachable_unchecked;
use core::ops::RangeBounds;

/// Helper struct for dungeon RNG.
pub struct DungeonRng(OverlayLoadLease<29>);

impl CreatableWithLease<29> for DungeonRng {
    fn _create(lease: OverlayLoadLease<29>) -> Self {
        Self(lease)
    }

    fn lease(&self) -> &OverlayLoadLease<29> {
        &self.0
    }
}

struct DungeonRngImpl;

impl Rng for DungeonRngImpl {
    fn rand16(&mut self) -> u16 {
        unsafe { ffi::DungeonRand16Bit() as u16 }
    }

    /// NOT SUPPORTED BY THIS.
    fn rand32(&mut self) -> u32 {
        // SAFETY: This will never be called. It is not used in this module and it is not exposed.
        unsafe { unreachable_unchecked() }
    }

    /// NOT SUPPORTED BY THIS.
    fn rand_range32(&mut self, _x: c_int, _y: c_int) -> c_int {
        // SAFETY: This will never be called. It is not used in this module and it is not exposed.
        unsafe { unreachable_unchecked() }
    }
}

impl DungeonRng {
    /// Generates a seed with which to initialize the dungeon PRNG.
    ///
    /// The seed is calculated by starting with a different seed, the "preseed" x0 (defaults to 1,
    /// but can be set by other functions). The preseed is iterated twice with the same recurrence
    /// relation used in the primary LCG to generate two pseudorandom 32-bit numbers x1 and x2.
    /// The output seed is then computed as
    ///
    /// ```text
    /// seed = (x1 & 0xFF0000) | (x2 >> 0x10) | 1
    /// ```
    ///
    /// The value x1 is then saved as the new preseed.
    ///
    /// This method of seeding the dungeon PRNG appears to be used only sometimes, depending on
    /// certain flags in the data for a given dungeon.
    pub fn generate_dungeon_rng_seed(&self) -> u32 {
        unsafe { ffi::GenerateDungeonRngSeed() }
    }

    /// Gets the current preseed stored in the global dungeon PRNG state.
    ///
    /// See [`Self::generate_dungeon_rng_seed`] for more information.
    pub fn get_dungeon_rng_preeseed(&self) -> u32 {
        unsafe { ffi::GetDungeonRngPreseed() }
    }

    /// Gets the current preseed stored in the global dungeon PRNG state.
    ///
    /// See [`Self::generate_dungeon_rng_seed`] for more information.
    pub fn set_dungeon_rng_preeseed(&mut self, seed: u32) {
        unsafe { ffi::SetDungeonRngPreseed(seed) }
    }

    /// Initialize (or reinitialize) the dungeon PRNG with a given seed. The primary LCG and the
    /// five secondary LCGs are initialized jointly, and with the same seed.
    pub fn init_dungeon_rng(&mut self, seed: u32) {
        unsafe { ffi::InitDungeonRng(seed) }
    }

    /// Generates a random number between the beginning and end of the range.
    /// If the range is unbounded, min and/or max values are bound to
    /// 0 ([`u16::MIN`]) and [`u16::MAX`] respectively.
    ///
    /// Note that this uses the dungeon PRNG as opposed to the functions in [`crate::api::random`].
    ///
    /// Random numbers are generated with a linear congruential generator (LCG). The game actually
    /// maintains 6 separate sequences that can be used for generation: a primary LCG and 5
    /// secondary LCGs. The generator used depends on parameters set on the global PRNG state.
    ///
    /// All dungeon LCGs have a modulus of 2^32 and a multiplier of 1566083941
    /// (see symbol DUNGEON_PRNG_LCG_MULTIPLIER).
    /// The primary LCG uses an increment of 1, while the secondary LCGs use an increment of
    /// 2531011 (see DUNGEON_PRNG_LCG_INCREMENT_SECONDARY symbol).
    ///
    /// So, for example, the primary LCG uses the recurrence relation:
    ///
    /// ```text
    /// x = (1566083941 * x_prev + 1) % 2^32
    /// ```
    ///
    /// Since the dungeon LCGs generate 32-bit integers rather than 16-bit, the primary LCG yields
    /// 16-bit values by taking the upper 16 bits of the computed 32-bit value. The secondary LCGs
    /// yield 16-bit values by taking the lower 16 bits of the computed 32-bit value.
    ///
    /// All of the dungeon LCGs have a hard-coded default seed of 1, but in practice the
    /// seed is set with a call to InitDungeonRng during dungeon initialization.
    ///
    /// The range must contain at least one element, or this will panic.
    /// Same if the start bound is excluded.
    pub fn rand_u16<R: RangeBounds<u16>>(&self, range: R) -> u16 {
        rand_u16_internal(&mut DungeonRngImpl, range)
    }

    /// Compute a pseudorandom integer on the interval [0, 100) using the dungeon PRNG.
    pub fn rand100(&self) -> u32 {
        unsafe { ffi::DungeonRand100() }
    }

    /// Returns the result of a possibly biased coin flip (a Bernoulli random variable) with some
    /// success probability `p`, using the dungeon PRNG
    /// (`true` has a probability `p`, `false` has (`1-p`)).
    ///
    /// `success_percentage` is `100*p`.
    pub fn rand_outcome(&self, success_percentage: i32) -> bool {
        unsafe { ffi::DungeonRandOutcome(success_percentage) > 0 }
    }

    /// Like [`Self::rand_outcome`], but specifically for user-target interactions.
    ///
    /// This modifies the underlying random process depending on factors like Serene Grace, and
    /// whether or not either entity has fainted.
    ///
    /// A percentage of 0 is treated specially and guarantees success.
    pub fn rand_outcome_user_target_interaction(
        &self,
        user: &DungeonEntity,
        target: &DungeonEntity,
        success_percentage: i32,
    ) -> bool {
        unsafe {
            ffi::DungeonRandOutcomeUserTargetInteraction(
                force_mut_ptr!(user),
                force_mut_ptr!(target),
                success_percentage,
            ) > 0
        }
    }

    /// Like [`Self::rand_outcome`], but specifically for user actions.
    ///
    /// This modifies the underlying random process to factor in Serene Grace (and checks whether
    /// the user is a valid entity).
    ///
    /// A percentage of 0 is treated specially and guarantees success.
    pub fn rand_outcome_user_action(&self, user: &DungeonEntity, success_percentage: i32) -> bool {
        unsafe { ffi::DungeonRandOutcomeUserAction(force_mut_ptr!(user), success_percentage) > 0 }
    }

    /// Sets the dungeon PRNG to use the primary LCG for subsequent random number generation.
    pub fn set_primary_rng(&mut self) {
        unsafe { ffi::DungeonRngSetPrimary() }
    }

    /// Sets the dungeon PRNG to use one of the 5 secondary LCGs for subsequent random number
    /// generation.
    pub fn set_secondary_rng(&mut self, idx: i32) {
        unsafe { ffi::DungeonRngSetSecondary(idx) }
    }

    /// Sets the dungeon PRNG to use the primary LCG for subsequent random number generation,
    /// and also resets the secondary LCG index back to 0.
    ///
    /// Similar to [`Self::set_primary_rng`], but it doesn't modify the secondary LCG
    /// index if it was already set to something other than 0.
    pub fn unset_secondary_rng(&mut self, idx: i32) {
        unsafe { ffi::DungeonRngSetSecondary(idx) }
    }
}