summaryrefslogtreecommitdiff
path: root/src/code/sys_rumble.c
blob: 06bef6e34956ca9d715a63a07ae1c8f1955c554d (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
/*
 * File: sys_rumble.c
 * Description: Internal scheduler system for rumble requests
 */

#include "global.h"
#include "z64rumble.h"

// sRumbleWasEnabledOnLastTick/sWillDisableRumble? Probably name it after updateEnabled
u8 D_801D1E70 = true;

void RumbleManager_Update(RumbleManager* rumbleMgr) {
    s32 strongestIndex = -1;
    s32 i;
    s32 temp;

    // Turn rumbling off for all controllers
    for (i = 0; i < ARRAY_COUNT(rumbleMgr->rumbleEnabled); i++) {
        rumbleMgr->rumbleEnabled[i] = false;
    }

    if (!rumbleMgr->updateEnabled) {
        // Rumbling update is disabled

        if (D_801D1E70) {
            for (i = 0; i < MAXCONTROLLERS; i++) {
                PadMgr_RumbleSetSingle(i, false);
            }
        }

        D_801D1E70 = rumbleMgr->updateEnabled;
        PadMgr_RumblePause();

        return;
    }

    D_801D1E70 = rumbleMgr->updateEnabled;

    // Start up the manager by wiping old requests
    if (rumbleMgr->state == RUMBLEMANAGER_STATE_INITIAL) {
        for (i = 0; i < MAXCONTROLLERS; i++) {
            PadMgr_RumbleSetSingle(i, false);
        }

        for (i = 0; i < RUMBLE_REQUEST_BUFFER_SIZE; i++) {
            rumbleMgr->requestIntensities[i] = 0;
            rumbleMgr->requestDecayTimers[i] = 0;
            rumbleMgr->requestDecaySteps[i] = 0;
            rumbleMgr->requestAccumulators[i] = 0;
        }

        rumbleMgr->rumblingDuration = 0;
        rumbleMgr->downTime = 0;

        rumbleMgr->overrideIntensity = 0;
        rumbleMgr->overrideDecayTimer = 0;
        rumbleMgr->overrideDecayStep = 0;
        rumbleMgr->overrideAccumulator = 0;

        rumbleMgr->state = RUMBLEMANAGER_STATE_RUNNING;

        PadMgr_RumblePause();
    }

    if (rumbleMgr->state != RUMBLEMANAGER_STATE_WIPE) {
        // Process arrays of rumble requests
        for (i = 0; i < RUMBLE_REQUEST_BUFFER_SIZE; i++) {
            if (rumbleMgr->requestIntensities[i] != 0) { // This entry has a non-empty rumble request
                if (rumbleMgr->requestDecayTimers[i] > 0) {
                    rumbleMgr->requestDecayTimers[i]--;
                } else {
                    temp = rumbleMgr->requestIntensities[i] - rumbleMgr->requestDecaySteps[i];
                    if (temp > 0) {
                        rumbleMgr->requestIntensities[i] = temp;
                    } else {
                        rumbleMgr->requestIntensities[i] = 0;
                    }
                }

                temp = rumbleMgr->requestAccumulators[i] + rumbleMgr->requestIntensities[i];
                rumbleMgr->requestAccumulators[i] = temp; // overflows
                if (strongestIndex == -1) {
                    strongestIndex = i;
                    rumbleMgr->rumbleEnabled[0] = (temp >= 0x100);
                } else if (rumbleMgr->requestIntensities[strongestIndex] < rumbleMgr->requestIntensities[i]) {
                    strongestIndex = i;
                    rumbleMgr->rumbleEnabled[0] = (temp >= 0x100);
                }
            }
        }

        // Process Override request. Note it takes priority over the values set by the request arrays
        if (rumbleMgr->overrideIntensity != 0) {
            if (rumbleMgr->overrideDecayTimer > 0) {
                rumbleMgr->overrideDecayTimer--;
            } else {
                temp = rumbleMgr->overrideIntensity - rumbleMgr->overrideDecayStep;
                if (temp > 0) {
                    rumbleMgr->overrideIntensity = temp;
                } else {
                    rumbleMgr->overrideIntensity = 0;
                }
            }

            temp = rumbleMgr->overrideAccumulator + rumbleMgr->overrideIntensity;
            rumbleMgr->overrideAccumulator = temp; // overflows
            rumbleMgr->rumbleEnabled[0] = (temp >= 0x100);
        }

        if (rumbleMgr->overrideIntensity != 0) {
            temp = rumbleMgr->overrideIntensity;
        } else if (strongestIndex == -1) {
            temp = 0;
        } else {
            temp = rumbleMgr->requestIntensities[strongestIndex];
        }

        // Keep track of how long this have been rumbling (almost) nonstop
        if (temp == 0) {
            rumbleMgr->downTime++;
            if (rumbleMgr->downTime > 5) {
                rumbleMgr->rumblingDuration = 0;
                rumbleMgr->downTime = 5;
            }
        } else {
            rumbleMgr->downTime = 0;
            rumbleMgr->rumblingDuration++;
            if (rumbleMgr->rumblingDuration > 2 * 60 * 60) { // 2 minutes
                // Rumbling has lasted too long, clear system
                rumbleMgr->state = RUMBLEMANAGER_STATE_WIPE;
            }
        }
    } else { // RUMBLEMANAGER_STATE_WIPE
        for (i = 0; i < RUMBLE_REQUEST_BUFFER_SIZE; i++) {
            rumbleMgr->requestIntensities[i] = 0;
            rumbleMgr->requestDecayTimers[i] = 0;
            rumbleMgr->requestDecaySteps[i] = 0;
            rumbleMgr->requestAccumulators[i] = 0;
        }

        rumbleMgr->rumblingDuration = 0;
        rumbleMgr->downTime = 0;

        rumbleMgr->overrideIntensity = 0;
        rumbleMgr->overrideDecayTimer = 0;
        rumbleMgr->overrideDecayStep = 0;
        rumbleMgr->overrideAccumulator = 0;

        PadMgr_RumblePause();
    }
}

void RumbleManager_Init(RumbleManager* rumbleMgr) {
    bzero(rumbleMgr, sizeof(RumbleManager));
    rumbleMgr->state = RUMBLEMANAGER_STATE_INITIAL;
    rumbleMgr->updateEnabled = true;
}

void RumbleManager_Destroy(RumbleManager* rumbleMgr) {
}