forked from vrmiguel/sm64-analog-camera
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgame.c
378 lines (329 loc) · 14.3 KB
/
game.c
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
#include <ultra64.h>
#include "sm64.h"
#include <stdio.h>
#include "audio/external.h"
#include "buffers/framebuffers.h"
#include "buffers/zbuffer.h"
#include "engine/level_script.h"
#include "main.h"
#include "memory.h"
#include "save_file.h"
#include "seq_ids.h"
#include "sound_init.h"
#include "display.h"
#include "profiler.h"
#include "print.h"
#include "segment2.h"
#include "main_entry.h"
#include "pc/controller/controller_api.h"
#include <prevent_bss_reordering.h>
#include "game.h"
// Analog camera movement by Pathétique (github.com/vrmiguel), y0shin and Mors
// Contribute or communicate bugs at github.com/vrmiguel/sm64-analog-camera
// FIXME: I'm not sure all of these variables belong in this file, but I don't
// know of a good way to split them
struct Controller gControllers[3];
OSContStatus gControllerStatuses[4];
OSContPad gControllerPads[4];
OSMesgQueue gGameVblankQueue;
OSMesgQueue D_80339CB8;
OSMesg D_80339CD0;
OSMesg D_80339CD4;
struct VblankHandler gGameVblankHandler;
uintptr_t gPhysicalFrameBuffers[3];
uintptr_t gPhysicalZBuffer;
void *D_80339CF0;
void *D_80339CF4;
struct SPTask *gGfxSPTask;
Gfx *gDisplayListHead;
u8 *gGfxPoolEnd;
struct GfxPool *gGfxPool;
u8 gControllerBits;
s8 gEepromProbe;
struct MarioAnimation D_80339D10;
struct MarioAnimation gDemo;
UNUSED u8 filler80339D30[0x90];
void (*D_8032C6A0)(void) = NULL;
struct Controller *gPlayer1Controller = &gControllers[0];
struct Controller *gPlayer2Controller = &gControllers[1];
// probably debug only, see note below
struct Controller *gPlayer3Controller = &gControllers[2];
struct DemoInput *gCurrDemoInput = NULL; // demo input sequence
u16 gDemoInputListID = 0;
struct DemoInput gRecordedDemoInput = { 0 }; // possibly removed in EU. TODO: Check
// this function records distinct inputs over a 255-frame interval to RAM locations and was likely
// used to record the demo sequences seen in the final game. This function is unused.
static void record_demo(void) {
// record the player's button mask and current rawStickX and rawStickY.
u8 buttonMask =
((gPlayer1Controller->buttonDown & (A_BUTTON | B_BUTTON | Z_TRIG | START_BUTTON)) >> 8)
| (gPlayer1Controller->buttonDown & (U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS));
s8 rawStickX = gPlayer1Controller->rawStickX;
s8 rawStickY = gPlayer1Controller->rawStickY;
// if the stick is in deadzone, set its value to 0 to
// nullify the effects. We do not record deadzone inputs.
if (rawStickX > -8 && rawStickX < 8) {
rawStickX = 0;
}
if (rawStickY > -8 && rawStickY < 8) {
rawStickY = 0;
}
// record the distinct input and timer so long as they
// are unique. If the timer hits 0xFF, reset the timer
// for the next demo input.
if (gRecordedDemoInput.timer == 0xFF || buttonMask != gRecordedDemoInput.buttonMask
|| rawStickX != gRecordedDemoInput.rawStickX || rawStickY != gRecordedDemoInput.rawStickY) {
gRecordedDemoInput.timer = 0;
gRecordedDemoInput.buttonMask = buttonMask;
gRecordedDemoInput.rawStickX = rawStickX;
gRecordedDemoInput.rawStickY = rawStickY;
}
gRecordedDemoInput.timer++;
}
// take the updated controller struct and calculate
// the new x, y, and distance floats.
void adjust_analog_stick(struct Controller *controller) {
UNUSED u8 pad[8];
// reset the controller's x and y floats.
controller->stickX = 0;
controller->stickY = 0;
// modulate the rawStickX and rawStickY to be the new f32 values by adding/subtracting 6.
if (controller->rawStickX <= -8) {
controller->stickX = controller->rawStickX + 6;
}
if (controller->rawStickX >= 8) {
controller->stickX = controller->rawStickX - 6;
}
if (controller->rawStickY <= -8) {
controller->stickY = controller->rawStickY + 6;
}
if (controller->rawStickY >= 8) {
controller->stickY = controller->rawStickY - 6;
}
// calculate f32 magnitude from the center by vector length.
controller->stickMag =
sqrtf(controller->stickX * controller->stickX + controller->stickY * controller->stickY);
// magnitude cannot exceed 64.0f: if it does, modify the values appropriately to
// flatten the values down to the allowed maximum value.
if (controller->stickMag > 64) {
controller->stickX *= 64 / controller->stickMag;
controller->stickY *= 64 / controller->stickMag;
controller->stickMag = 64;
}
}
// if a demo sequence exists, this will run the demo
// input list until it is complete. called every frame.
void run_demo_inputs(void) {
// eliminate the unused bits.
gControllers[0].controllerData->button &= VALID_BUTTONS;
/*
Check if a demo inputs list
exists and if so, run the
active demo input list.
*/
if (gCurrDemoInput != NULL) {
/*
clear player 2's inputs if they exist. Player 2's controller
cannot be used to influence a demo. At some point, Nintendo
may have planned for there to be a demo where 2 players moved
around instead of just one, so clearing player 2's influence from
the demo had to have been necessary to perform this. Co-op mode, perhaps?
*/
if (gControllers[1].controllerData != NULL) {
gControllers[1].controllerData->stick_x = 0;
gControllers[1].controllerData->stick_y = 0;
gControllers[1].controllerData->button = 0;
}
// the timer variable being 0 at the current input means the demo is over.
// set the button to the END_DEMO mask to end the demo.
if (gCurrDemoInput->timer == 0) {
gControllers[0].controllerData->stick_x = 0;
gControllers[0].controllerData->stick_y = 0;
gControllers[0].controllerData->button = END_DEMO;
} else {
// backup the start button if it is pressed, since we don't want the
// demo input to override the mask where start may have been pressed.
u16 startPushed = gControllers[0].controllerData->button & START_BUTTON;
// perform the demo inputs by assigning the current button mask and the stick inputs.
gControllers[0].controllerData->stick_x = gCurrDemoInput->rawStickX;
gControllers[0].controllerData->stick_y = gCurrDemoInput->rawStickY;
/*
to assign the demo input, the button information is stored in
an 8-bit mask rather than a 16-bit mask. this is because only
A, B, Z, Start, and the C-Buttons are used in a demo, as bits
in that order. In order to assign the mask, we need to take the
upper 4 bits (A, B, Z, and Start) and shift then left by 8 to
match the correct input mask. We then add this to the masked
lower 4 bits to get the correct button mask.
*/
gControllers[0].controllerData->button =
((gCurrDemoInput->buttonMask & 0xF0) << 8) + ((gCurrDemoInput->buttonMask & 0xF));
// if start was pushed, put it into the demo sequence being input to
// end the demo.
gControllers[0].controllerData->button |= startPushed;
// run the current demo input's timer down. if it hits 0, advance the
// demo input list.
if (--gCurrDemoInput->timer == 0) {
gCurrDemoInput++;
}
}
}
}
// initialize the controller structs to point at the OSCont information.
void read_controller_inputs(void) {
s32 i;
// if any controllers are plugged in, update the
// controller information.
if (gControllerBits) {
osRecvMesg(&gSIEventMesgQueue, &D_80339BEC, OS_MESG_BLOCK);
osContGetReadData(&gControllerPads[0]);
}
run_demo_inputs();
//printf("P2: %d, %d\n", c_rightx, c_righty); // is righty broken?
//struct Controller *controller_backup;
for (i = 0; i < 2; i++)
{
struct Controller *controller = &gControllers[i];
if (i==1) // This is related to the analog camera control, using a P2 controller hack. P2 will no longer be correctly available for multiplayer.
{
controller->rawStickX = c_rightx;
controller->rawStickY = c_righty;
controller->stickX = c_rightx;
controller->stickY = c_righty;
}
else
{
// if we're receiving inputs, update the controller struct
// with the new button info.
if (controller->controllerData != NULL) {
controller->rawStickX = controller->controllerData->stick_x;
controller->rawStickY = controller->controllerData->stick_y;
controller->buttonPressed = controller->controllerData->button
& (controller->controllerData->button ^ controller->buttonDown);
// 0.5x A presses are a good meme
controller->buttonDown = controller->controllerData->button;
adjust_analog_stick(controller);
} else // otherwise, if the controllerData is NULL, 0 out all of the inputs.
{
controller->rawStickX = 0;
controller->rawStickY = 0;
controller->buttonPressed = 0;
controller->buttonDown = 0;
controller->stickX = 0;
controller->stickY = 0;
controller->stickMag = 0;
}
}
}
// For some reason, player 1's inputs are copied to player 3's port. This
// potentially may have been a way the developers "recorded" the inputs
// for demos, despite record_demo existing.
gPlayer3Controller->rawStickX = gPlayer1Controller->rawStickX;
gPlayer3Controller->rawStickY = gPlayer1Controller->rawStickY;
gPlayer3Controller->stickX = gPlayer1Controller->stickX;
gPlayer3Controller->stickY = gPlayer1Controller->stickY;
gPlayer3Controller->stickMag = gPlayer1Controller->stickMag;
gPlayer3Controller->buttonPressed = gPlayer1Controller->buttonPressed;
gPlayer3Controller->buttonDown = gPlayer1Controller->buttonDown;
}
// initialize the controller structs to point at the OSCont information.
void init_controllers(void) {
s16 port, cont;
// set controller 1 to point to the set of status/pads for input 1 and
// init the controllers.
gControllers[0].statusData = &gControllerStatuses[0];
gControllers[0].controllerData = &gControllerPads[0];
osContInit(&gSIEventMesgQueue, &gControllerBits, &gControllerStatuses[0]);
// strangely enough, the EEPROM probe for save data is done in this function.
// save pak detection?
gEepromProbe = osEepromProbe(&gSIEventMesgQueue);
// loop over the 4 ports and link the controller structs to the appropriate
// status and pad. Interestingly, although there are pointers to 3 controllers,
// only 2 are connected here. The third seems to have been reserved for debug
// purposes and was never connected in the retail ROM, thus gPlayer3Controller
// cannot be used, despite being referenced in various code.
for (cont = 0, port = 0; port < 4 && cont < 2; port++) {
// is controller plugged in?
if (gControllerBits & (1 << port)) {
// the game allows you to have just 1 controller plugged
// into any port in order to play the game. this was probably
// so if any of the ports didnt work, you can have controllers
// plugged into any of them and it will work.
gControllers[cont].statusData = &gControllerStatuses[port];
gControllers[cont++].controllerData = &gControllerPads[port];
}
}
}
void setup_game_memory(void) {
UNUSED u8 pad[8];
set_segment_base_addr(0, (void *) 0x80000000);
osCreateMesgQueue(&D_80339CB8, &D_80339CD4, 1);
osCreateMesgQueue(&gGameVblankQueue, &D_80339CD0, 1);
gPhysicalZBuffer = VIRTUAL_TO_PHYSICAL(gZBuffer);
gPhysicalFrameBuffers[0] = VIRTUAL_TO_PHYSICAL(gFrameBuffer0);
gPhysicalFrameBuffers[1] = VIRTUAL_TO_PHYSICAL(gFrameBuffer1);
gPhysicalFrameBuffers[2] = VIRTUAL_TO_PHYSICAL(gFrameBuffer2);
D_80339CF0 = main_pool_alloc(0x4000, MEMORY_POOL_LEFT);
set_segment_base_addr(17, (void *) D_80339CF0);
func_80278A78(&D_80339D10, gMarioAnims, D_80339CF0);
D_80339CF4 = main_pool_alloc(2048, MEMORY_POOL_LEFT);
set_segment_base_addr(24, (void *) D_80339CF4);
func_80278A78(&gDemo, gDemoInputs, D_80339CF4);
load_segment(0x10, _entrySegmentRomStart, _entrySegmentRomEnd, MEMORY_POOL_LEFT);
load_segment_decompress(2, _segment2_mio0SegmentRomStart, _segment2_mio0SegmentRomEnd);
}
#ifndef TARGET_N64
static struct LevelCommand *levelCommandAddr;
#endif
// main game loop thread. runs forever as long as the game
// continues.
void thread5_game_loop(UNUSED void *arg) {
#ifdef TARGET_N64
struct LevelCommand *levelCommandAddr;
#endif
setup_game_memory();
init_controllers();
save_file_load_all();
set_vblank_handler(2, &gGameVblankHandler, &gGameVblankQueue, (OSMesg) 1);
// point levelCommandAddr to the entry point into the level script data.
levelCommandAddr = segmented_to_virtual(level_script_entry);
play_music(2, SEQUENCE_ARGS(0, SEQ_SOUND_PLAYER), 0);
set_sound_mode(save_file_get_sound_mode());
#ifdef TARGET_N64
func_80247ED8();
while (1) {
#else
gGlobalTimer++;
}
void game_loop_one_iteration(void) {
#endif
// if the reset timer is active, run the process to reset the game.
if (gResetTimer) {
func_80247D84();
#ifdef TARGET_N64
continue;
#else
return;
#endif
}
profiler_log_thread5_time(THREAD5_START);
// if any controllers are plugged in, start read the data for when
// read_controller_inputs is called later.
if (gControllerBits) {
osContStartReadData(&gSIEventMesgQueue);
}
audio_game_loop_tick();
func_80247FAC();
read_controller_inputs();
levelCommandAddr = level_script_execute(levelCommandAddr);
display_and_vsync();
// when debug info is enabled, print the "BUF %d" information.
if (gShowDebugText) {
// subtract the end of the gfx pool with the display list to obtain the
// amount of free space remaining.
print_text_fmt_int(180, 20, "BUF %d", gGfxPoolEnd - (u8 *) gDisplayListHead);
}
#ifdef TARGET_N64
}
#endif
}