summaryrefslogtreecommitdiff
path: root/src/KingSystem/System/VFR.h
blob: 1f69fbdc664fc2e6e8f2e73370d9cfa4661ab871 (plain)
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
#pragma once

#include <basis/seadTypes.h>
#include <cmath>
#include <container/seadBuffer.h>
#include <container/seadSafeArray.h>
#include <heap/seadDisposer.h>
#include <math/seadMathCalcCommon.h>
#include <mc/seadCoreInfo.h>
#include <prim/seadBitFlag.h>
#include "KingSystem/System/CoreInfo.h"
#include "KingSystem/Utils/Types.h"

namespace ksys {

class VFR {
    SEAD_SINGLETON_DISPOSER(VFR)
    VFR();
    virtual ~VFR();

public:
    struct TimeSpeedMultiplier {
        TimeSpeedMultiplier();
        ~TimeSpeedMultiplier();
        TimeSpeedMultiplier(const TimeSpeedMultiplier&) = default;
        TimeSpeedMultiplier(TimeSpeedMultiplier&&) = default;
        TimeSpeedMultiplier& operator=(const TimeSpeedMultiplier&) = default;
        TimeSpeedMultiplier& operator=(TimeSpeedMultiplier&&) = default;

        void update(f32 multiplier);

        bool is_custom = false;
        f32 value = 1.0;
        f32 target_value = 1.0;
    };

    /// Changes the delta timing and restores the original value when going out of scope.
    struct ScopedDeltaSetter {
        ScopedDeltaSetter();
        ScopedDeltaSetter(u32 include_mask, u32 exclude_mask);
        ~ScopedDeltaSetter();
        ScopedDeltaSetter(const ScopedDeltaSetter&) = delete;
        ScopedDeltaSetter(ScopedDeltaSetter&&) = delete;
        auto operator=(const ScopedDeltaSetter&) = delete;
        auto operator=(ScopedDeltaSetter&&) = delete;
        void set(u32 include_mask, u32 exclude_mask);

        f32 mTimeRate = 1.0;
        f32 mPreviousDelta = 0.0;
    };

    void init(u32 interval, int num_speed_multipliers, sead::Heap* heap, u32 mask);

    void setIntervalOverride(u32 interval);
    void clearIntervalOverride();
    bool hasIntervalOverride() const;
    u32 getIntervalOverride() const;

    void updateInterval(u32 new_interval);

    void useBufferB();
    void useBufferA();

    f32 getDeltaAndSetMin(f32* raw_delta_frames, u32 include_mask, u32 exclude_mask);
    void resetTimeMultipliers();
    bool hasCustomTimeMultiplier() const;
    // TODO: requires ksys::Sound
    void setTimeMultiplier(u32 idx, f32 multiplier);
    // TODO: requires ksys::Sound
    void resetTimeMultiplier(u32 idx);

    f32 getDeltaFrame(u32 core) const { return *mDeltaFrames[core]; }
    f32 getDeltaFrame() const { return getDeltaFrame(sead::CoreInfo::getCurrentCoreId()); }

    f32 getDeltaTime(u32 core) const { return *mDeltaTimes[core]; }
    f32 getDeltaTime() const { return getDeltaTime(sead::CoreInfo::getCurrentCoreId()); }

    f32 getIntervalRatio(u32 core) const { return *mIntervalRatios[core]; }
    f32 getIntervalRatio() const { return getIntervalRatio(sead::CoreInfo::getCurrentCoreId()); }

    template <typename T>
    static inline void add(T* value, const T& v) {
        *value += v * instance()->getDeltaFrame();
    }

    template <typename T>
    static inline void multiply(T* value, f32 scalar) {
        *value *= std::pow(scalar, instance()->getDeltaFrame());
    }

    static inline f32 getLerpFactor(f32 t) {
        return 1.0f - std::pow(1.0f - t, instance()->getDeltaFrame());
    }

    template <typename T>
    static inline void lerp(T* value, const T& b, f32 t) {
        *value += getLerpFactor(t) * (b - *value);
    }

    template <typename T>
    static inline void lerp(T* value, const T& b, f32 t, f32 max_delta) {
        const auto f = getLerpFactor(t);
        const auto max_d = instance()->getDeltaFrame() * max_delta;
        const auto diff = b - *value;
        const auto d = f * sead::Mathf::abs(diff);
        if (d > max_d)
            *value += diff < 0.0 ? -max_d : max_d;
        else
            *value += f * diff;
    }

    template <typename T>
    static inline bool lerp(T* value, const T& b, f32 t, f32 max_delta, f32 min_delta) {
        const auto f = getLerpFactor(t);
        const auto max_d = instance()->getDeltaFrame() * max_delta;
        const auto min_d = instance()->getDeltaFrame() * min_delta;

        const auto diff = b - *value;
        const auto d = f * sead::Mathf::abs(diff);

        if (sead::Mathf::abs(diff) <= min_d) {
            *value = b;
            return true;
        }

        if (d > max_d) {
            *value += diff < 0.0 ? -max_d : max_d;
        } else if (d < min_d) {
            *value += diff < 0.0 ? -min_d : min_d;
        } else {
            *value = *value + f * diff;
        }
        return false;
    }

    template <typename VectorT>
    static inline bool chaseVec(VectorT* value, const VectorT& target, f32 t) {
        const auto delta = instance()->getDeltaFrame() * t;
        const auto diff = target - *value;
        const auto norm = diff.length();

        if (norm <= delta) {
            value->e = target.e;
            return true;
        }

        value->setScaleAdd(delta, (1.0f / norm) * diff, *value);
        return false;
    }

private:
    struct TimeSpeedMultipliers : sead::Buffer<TimeSpeedMultiplier> {
        TimeSpeedMultipliers() {
            mBuffer = nullptr;
            mSize = 0;
        }
    };

    void setDelta(u32 core, f32 delta);
    void setDelta(f32 delta) { setDelta(sead::CoreInfo::getCurrentCoreId(), delta); }
    void setMinDelta(u32 core, const sead::BitFlag32& mask);
    void setMinDelta();
    void copyAtoB();

    bool mHasIntervalChanged = false;
    bool mHasIntervalOverride = false;
    u32 mIntervalOverride = 1;
    f32 mNewIntervalRatio = 1.0;
    u32 mIntervalA = 1;
    u32 mIntervalB = 1;
    f32 mIntervalRatioA = 1.0;
    f32 mIntervalRatioB = 1.0;

    sead::SafeArray<u32*, NumCores> mIntervals;
    sead::SafeArray<f32*, NumCores> mIntervalRatios;

    TimeSpeedMultipliers mTimeSpeedMultipliers;
    sead::BitFlag32 mMask = 0xffffffff;

    struct Storage {
        sead::SafeArray<f32, NumCores> raw_delta_frames{};
        sead::SafeArray<f32, NumCores> delta_frames{};
        sead::SafeArray<f32, NumCores> raw_delta_times{};
        sead::SafeArray<f32, NumCores> delta_times{};
    };
    sead::SafeArray<Storage, 2> mStorage{};

    /// Delta frames.
    sead::SafeArray<f32*, NumCores> mRawDeltaFrames;
    /// Delta frames, adjusted for present interval changes.
    sead::SafeArray<f32*, NumCores> mDeltaFrames;
    /// Delta times. Equals raw_delta_frame * frame_time.
    sead::SafeArray<f32*, NumCores> mRawDeltaTimes;
    /// Delta times, adjusted for present interval changes. Equals delta_frame * frame_time.
    sead::SafeArray<f32*, NumCores> mDeltaTimes;
    /// Present interval.
    u32 mInterval = 1;
    /// Frames per second.
    u32 mFrameRate = 60;
    /// Second per frame.
    f32 mFrameTime = 1.0 / 60.0;
};
KSYS_CHECK_SIZE_NX150(VFR, 0x160);

}  // namespace ksys