Skip to content

Commit bb8d234

Browse files
authored
Spinout functions (#762)
* Document steering * Refer to steering position, instead of steering angle Refer to steering position instead of steering angle, to make clear that it is not the same thing as degrees. * Change comment to block format * Pre-spinout swerving When you sharply move a joystick in 150CC or hit a banana, you can enter a pre-spinout state where you swerve back and forth. This primarily documents how the swerving works, as well as updating some related function / property names
1 parent c68b3d4 commit bb8d234

8 files changed

Lines changed: 125 additions & 115 deletions

File tree

include/common_structs.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ typedef struct {
273273
/* 0x0064 */ Vec3f unk_064;
274274
/* 0x0070 */ f32 boundingBoxSize;
275275
/* 0x0074 */ f32 unk_074;
276-
/* 0x0078 */ s16 unk_078;
276+
/* 0x0078 */ s16 unk_078; // One source of angular velocity (turning / swerving)
277277
/* 0x007A */ s16 unk_07A;
278278
/* 0x007C */ s32 steerPosition; // Where the kart is steering, related to joystick position. Basically, in [-53 to 53], but shifted 16 bits left
279279
/* 0x0080 */ f32 boostPower;
@@ -288,13 +288,13 @@ typedef struct {
288288
/* 0x00A4 */ f32 unk_0A4;
289289
/* 0x00A8 */ s16 unk_0A8;
290290
/* 0x00AA */ s16 unk_0AA;
291-
/* 0x00AC */ s16 unk_0AC;
292-
/* 0x00AE */ s16 unk_0AE;
291+
/* 0x00AC */ s16 swerveDirection;
292+
/* 0x00AE */ s16 unk_0AE; // preserved rotation velocity? To restore after effects wear off
293293
/* 0x00B0 */ s16 unk_0B0;
294294
/* 0x00B2 */ s16 unk_0B2;
295-
/* 0x00B4 */ u16 unk_0B4;
295+
/* 0x00B4 */ u16 swerveTimer;
296296
/* 0x00B6 */ u16 kartGraphics;
297-
/* 0x00B8 */ f32 unk_0B8;
297+
/* 0x00B8 */ f32 swerveAccelInit;
298298
/* 0x00BC */ u32 effects;
299299
/* 0x00C0 */ s16 unk_0C0;
300300
/* 0x00C2 */ s16 unk_0C2;

include/defines.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ player spins. Something with avoding rollover of aniamation frame data? */
395395
#define UNUSED_0x1000 0x1000 // 0x1000 locked behind 0x400 (func_8002B830 -> func_800911B4)
396396
#define UNUSED_0x2000 0x2000 // 0x2000 locked behind 0x400 and 0x800 (func_8002B830 -> func_800911B4, apply_effect -> func_80091298,
397397
// func_80091440)
398-
#define DRIVING_SPINOUT 0x4000
398+
#define DRIVING_NEAR_SPINOUT 0x4000
399399
#define UNKNOWN_BATTLE_VAR 0x8000 // 0x8000 something battle related, unclear if ever set
400400

401401
/*

src/audio/external.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2385,7 +2385,7 @@ void func_800C70A8(u8 playerId) {
23852385
((gPlayers[playerId].effects & BANANA_NEAR_SPINOUT_EFFECT) == BANANA_NEAR_SPINOUT_EFFECT) ||
23862386
((gPlayers[playerId].effects & BANANA_SPINOUT_EFFECT) == BANANA_SPINOUT_EFFECT) ||
23872387
((gPlayers[playerId].effects & DRIVING_SPINOUT_EFFECT) == DRIVING_SPINOUT_EFFECT) ||
2388-
((gPlayers[playerId].kartProps & DRIVING_SPINOUT) == DRIVING_SPINOUT)) {
2388+
((gPlayers[playerId].kartProps & DRIVING_NEAR_SPINOUT) == DRIVING_NEAR_SPINOUT)) {
23892389
D_800E9E74[playerId] = 0x00000012;
23902390
}
23912391
if ((((gPlayers[playerId].effects & AB_SPIN_EFFECT) == AB_SPIN_EFFECT) &&

src/code_80057C60.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6258,7 +6258,7 @@ void func_8006C6AC(Player* player, s16 particleIndex, s8 playerId, s8 arg3) {
62586258
func_8005F90C(player, particleIndex, sp28, playerIdCopy, arg3);
62596259
} else if (((player->effects & EARLY_START_SPINOUT_EFFECT) && !(player->type & PLAYER_START_SEQUENCE)) ||
62606260
(player->effects & BANANA_NEAR_SPINOUT_EFFECT) || (player->effects & AB_SPIN_EFFECT) ||
6261-
(player->kartProps & DRIVING_SPINOUT)) {
6261+
(player->kartProps & DRIVING_NEAR_SPINOUT)) {
62626262
func_8005ED48(player, particleIndex, sp28, playerIdCopy, arg3);
62636263
} else {
62646264
setup_tyre_particles(player, particleIndex, sp28, playerIdCopy, arg3);
@@ -6398,7 +6398,7 @@ void func_8006CEC0(Player* arg0, s16 arg1, s8 playerId, s8 arg3) {
63986398
break;
63996399
}
64006400
} else {
6401-
if ((arg0->kartProps & BECOME_INVISIBLE) && (arg0->type & DRIVING_SPINOUT)) {
6401+
if ((arg0->kartProps & BECOME_INVISIBLE) && (arg0->type & DRIVING_NEAR_SPINOUT)) {
64026402
func_80061224(arg0, arg1, sp20, playerId, arg3);
64036403
return;
64046404
} else if (((arg0->effects & LIGHTNING_EFFECT) == LIGHTNING_EFFECT) && (arg0->unk_0B0 < 0x32)) {

src/effects.c

Lines changed: 92 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,13 @@ void clean_effect(Player* player, s8 playerIndex) {
152152

153153
if (((player->effects & BANANA_SPINOUT_EFFECT) == BANANA_SPINOUT_EFFECT) ||
154154
(player->effects & DRIVING_SPINOUT_EFFECT) == DRIVING_SPINOUT_EFFECT) {
155-
func_8008C8C4(player, playerIndex);
155+
remove_spinout_effects(player, playerIndex);
156156
}
157157
if ((player->effects & BANANA_NEAR_SPINOUT_EFFECT) == BANANA_NEAR_SPINOUT_EFFECT) {
158-
func_8008D0E4(player, playerIndex);
158+
remove_banana_near_spinout_effect(player, playerIndex);
159159
}
160-
if ((player->kartProps & DRIVING_SPINOUT) != 0) {
161-
func_8008D3B0(player, playerIndex);
160+
if ((player->kartProps & DRIVING_NEAR_SPINOUT) != 0) {
161+
remove_driving_near_spinout_effect(player, playerIndex);
162162
}
163163
if ((player->effects & MUSHROOM_EFFECT) == MUSHROOM_EFFECT) {
164164
remove_mushroom_effect(player);
@@ -257,13 +257,13 @@ void func_8008C6D0(Player* player, s8 playerIndex) {
257257
player->unk_042 = 0;
258258
}
259259

260-
void func_8008C73C(Player* player, s8 playerIndex) {
260+
void add_spinout_effect(Player* player, s8 playerIndex) {
261261
clean_effect(player, playerIndex);
262262
if (((player->effects & BANANA_SPINOUT_EFFECT) != BANANA_SPINOUT_EFFECT) &&
263263
((player->effects & DRIVING_SPINOUT_EFFECT) != DRIVING_SPINOUT_EFFECT)) {
264264
player->effects &= ~DRIFTING_EFFECT;
265265

266-
if ((player->unk_0C0 / 182) >= 0) {
266+
if ((player->unk_0C0 / DEGREES_CONVERSION_FACTOR) >= 0) {
267267
player->effects |= DRIVING_SPINOUT_EFFECT;
268268
} else {
269269
player->effects |= BANANA_SPINOUT_EFFECT;
@@ -293,7 +293,7 @@ void func_8008C73C(Player* player, s8 playerIndex) {
293293
}
294294
}
295295

296-
void func_8008C8C4(Player* player, s8 playerId) {
296+
void remove_spinout_effects(Player* player, s8 playerId) {
297297
player->effects &= ~BANANA_SPINOUT_EFFECT;
298298
player->effects &= ~DRIVING_SPINOUT_EFFECT;
299299
player->unk_0A8 = 0;
@@ -347,7 +347,7 @@ void func_8008C9EC(Player* player, s8 playerIndex) {
347347
if (gModeSelection == BATTLE) {
348348
pop_player_balloon(player, playerIndex);
349349
}
350-
func_8008C8C4(player, playerIndex);
350+
remove_spinout_effects(player, playerIndex);
351351
}
352352
}
353353
} else {
@@ -357,7 +357,7 @@ void func_8008C9EC(Player* player, s8 playerIndex) {
357357
if (stackPadding2 == 0) {
358358
player->unk_0B2--;
359359
if (player->unk_0B2 <= 0) {
360-
func_8008C8C4(player, playerIndex);
360+
remove_spinout_effects(player, playerIndex);
361361
if (gModeSelection == BATTLE) {
362362
pop_player_balloon(player, playerIndex);
363363
}
@@ -372,141 +372,151 @@ void func_8008C9EC(Player* player, s8 playerIndex) {
372372
}
373373
}
374374

375-
void func_8008CDC0(Player* player, s8 playerIndex) {
375+
void trigger_hit_banana(Player* player, s8 playerIndex) {
376376
clean_effect(player, playerIndex);
377377

378378
player->triggers &= ~HIT_BANANA_TRIGGER;
379-
player->unk_0B4 = 0;
380-
player->unk_0B8 = 3.0f;
381-
player->unk_0AC = 1;
379+
player->swerveTimer = 0;
380+
player->swerveAccelInit = 3.0f;
381+
player->swerveDirection = 1;
382382
player->effects &= ~DRIFTING_EFFECT;
383383

384384
if (((player->steerPosition >> 16) >= 20) || ((player->steerPosition >> 16) <= -20) ||
385385
(((player->speed / 18.0f) * 216.0f) <= 30.0f) || ((player->effects & MIDAIR_EFFECT) != 0) ||
386386
(((player->type & PLAYER_HUMAN) == 0) && ((player->effects & LOST_RACE_EFFECT) == 0))) {
387-
func_8008C73C(player, playerIndex);
387+
add_spinout_effect(player, playerIndex);
388388
} else {
389389
player->effects |= BANANA_NEAR_SPINOUT_EFFECT;
390390
}
391391
}
392392

393-
void func_8008CEB0(Player* player, s8 playerIndex) {
394-
f32 var_f0;
395-
s16 var_v1;
396-
s16 var_a3;
397-
s16 temp_f16;
398-
399-
var_f0 = player->unk_0B8;
400-
var_v1 = player->unk_0B4;
401-
var_a3 = player->unk_0AC;
402-
var_v1++;
403-
temp_f16 = (var_v1 * var_f0) - (0.2 * (var_v1 * var_v1));
404-
if ((var_v1 != 0) && (temp_f16 < 0)) {
405-
var_v1 = 0;
406-
var_a3 = -var_a3;
407-
var_f0 *= 0.8;
393+
// almost identical to apply_driving_near_spinout_effect, see there for more documentation
394+
void apply_banana_near_spinout_effect(Player* player, s8 playerIndex) {
395+
f32 swerve_accel_init;
396+
s16 swerve_timer;
397+
s16 swerve_direction;
398+
s16 swerve_velo_current;
399+
400+
swerve_accel_init = player->swerveAccelInit;
401+
swerve_timer = player->swerveTimer;
402+
swerve_direction = player->swerveDirection;
403+
swerve_timer++;
404+
swerve_velo_current = (swerve_timer * swerve_accel_init) - (0.2 * (swerve_timer * swerve_timer));
405+
if ((swerve_timer != 0) && (swerve_velo_current < 0)) {
406+
swerve_timer = 0;
407+
swerve_direction = -swerve_direction;
408+
swerve_accel_init *= 0.8;
409+
// requires braking to recover. A driving spinout allows just releasing gas as well
408410
if ((player->effects & BRAKING_EFFECT) == BRAKING_EFFECT) {
409411
player->effects |= BANANA_SPINOUT_SAVE_EFFECT;
410412
}
411-
if (var_f0 <= 1.0f) {
413+
if (swerve_accel_init <= 1.0f) {
412414
player->effects &= ~BANANA_NEAR_SPINOUT_EFFECT;
413415
if ((player->effects & BANANA_SPINOUT_SAVE_EFFECT) != BANANA_SPINOUT_SAVE_EFFECT) {
414-
func_8008C73C(player, playerIndex);
415-
var_v1 = 0;
416+
add_spinout_effect(player, playerIndex);
417+
swerve_timer = 0;
416418
} else {
417419
player->kartGraphics |= WHISTLE;
418420
player->effects &= ~BANANA_SPINOUT_SAVE_EFFECT;
419421
if ((player->type & PLAYER_HUMAN) == PLAYER_HUMAN) {
420422
func_800C90F4(playerIndex, (player->characterId * 0x10) + 0x29008008);
421-
var_v1 = 0;
423+
swerve_timer = 0;
422424
}
423425
}
424426
}
425427
}
426-
temp_f16 *= var_a3;
427-
if ((temp_f16 <= 0) && (var_a3 == 1)) {
428-
temp_f16 = 0;
428+
swerve_velo_current *= swerve_direction;
429+
if ((swerve_velo_current <= 0) && (swerve_direction == 1)) {
430+
swerve_velo_current = 0;
429431
}
430-
if ((temp_f16 >= 0) && (var_a3 == -1)) {
431-
temp_f16 = 0;
432+
if ((swerve_velo_current >= 0) && (swerve_direction == -1)) {
433+
swerve_velo_current = 0;
432434
}
433-
player->unk_078 += temp_f16 * 0x12;
434-
player->unk_0B8 = var_f0;
435-
player->unk_0B4 = var_v1;
436-
player->unk_0AC = var_a3;
435+
player->unk_078 += swerve_velo_current * 18;
436+
player->swerveAccelInit = swerve_accel_init;
437+
player->swerveTimer = swerve_timer;
438+
player->swerveDirection = swerve_direction;
437439
if (player->effects & MIDAIR_EFFECT) {
438-
func_8008C73C(player, playerIndex);
440+
add_spinout_effect(player, playerIndex);
439441
player->effects &= ~BANANA_NEAR_SPINOUT_EFFECT;
440442
}
441443
}
442444

443-
void func_8008D0E4(Player* player, UNUSED s8 playerIndex) {
445+
void remove_banana_near_spinout_effect(Player* player, UNUSED s8 playerIndex) {
444446
player->effects &= ~BANANA_NEAR_SPINOUT_EFFECT;
445447
}
446448

447-
void func_8008D0FC(Player* player, s8 playerIndex) {
449+
void trigger_driving_spinout(Player* player, s8 playerIndex) {
448450
clean_effect(player, playerIndex);
449451

450452
player->triggers &= ~DRIVING_SPINOUT_TRIGGER;
451-
player->unk_0B4 = 0;
452-
player->unk_0B8 = 2.0f;
453-
player->unk_0AC = 1;
453+
player->swerveTimer = 0;
454+
player->swerveAccelInit = 2.0f;
455+
player->swerveDirection = 1;
454456
player->effects &= ~DRIFTING_EFFECT;
455-
player->kartProps |= DRIVING_SPINOUT;
457+
player->kartProps |= DRIVING_NEAR_SPINOUT;
456458
}
457459

458-
void func_8008D170(Player* player, s8 playerIndex) {
459-
f32 var_f0;
460-
s16 var_v1;
461-
s16 var_a3;
462-
s16 temp_f16;
463-
464-
var_f0 = player->unk_0B8;
465-
var_v1 = player->unk_0B4;
466-
var_a3 = player->unk_0AC;
467-
var_v1++;
468-
temp_f16 = (var_v1 * var_f0) - (0.1 * (var_v1 * var_v1));
469-
if ((var_v1 != 0) && (temp_f16 < 0)) {
470-
var_v1 = 0;
471-
var_a3 = -var_a3;
472-
var_f0 *= 0.9;
460+
void apply_driving_near_spinout_effect(Player* player, s8 playerIndex) {
461+
f32 swerve_accel_init;
462+
s16 swerve_timer;
463+
s16 swerve_direction;
464+
s16 swerve_velo_current;
465+
466+
// These properties are only used for swerving before spinouts
467+
swerve_accel_init = player->swerveAccelInit;
468+
swerve_timer = player->swerveTimer;
469+
swerve_direction = player->swerveDirection;
470+
swerve_timer++;
471+
472+
// Standard physics formula: Velo_current = velo_init + (accel_init * time) + (accel_jerk * time**2) / 2
473+
swerve_velo_current = (swerve_accel_init * swerve_timer) - (0.1 * (swerve_timer * swerve_timer));
474+
475+
// Once one swerve finishes, setup to start a smaller one in the opposite direction
476+
if ((swerve_timer != 0) && (swerve_velo_current < 0)) { // (10 * swerve_accel_init < swerve_timer))
477+
swerve_timer = 0;
478+
swerve_direction = -swerve_direction;
479+
swerve_accel_init *= 0.9;
473480
if (((player->effects & BRAKING_EFFECT) == BRAKING_EFFECT) || !(player->kartProps & THROTTLE)) {
474481
player->effects |= BANANA_SPINOUT_SAVE_EFFECT;
475482
}
476-
if (var_f0 <= 1.3) {
477-
player->kartProps &= ~DRIVING_SPINOUT;
483+
// stop swerving once they are small enough
484+
if (swerve_accel_init <= 1.3) {
485+
player->kartProps &= ~DRIVING_NEAR_SPINOUT;
478486
if ((player->effects & BANANA_SPINOUT_SAVE_EFFECT) != BANANA_SPINOUT_SAVE_EFFECT) {
479-
func_8008C73C(player, playerIndex);
480-
var_v1 = 0;
487+
add_spinout_effect(player, playerIndex);
488+
swerve_timer = 0;
481489
} else {
482490
player->kartGraphics |= WHISTLE;
483491
player->effects &= ~BANANA_SPINOUT_SAVE_EFFECT;
484492
if ((player->type & PLAYER_HUMAN) == PLAYER_HUMAN) {
485493
func_800C90F4(playerIndex, (player->characterId * 0x10) + 0x29008008);
486-
var_v1 = 0;
494+
swerve_timer = 0;
487495
}
488496
}
489497
}
490498
}
491-
temp_f16 *= var_a3;
492-
if ((temp_f16 <= 0) && (var_a3 == 1)) {
493-
temp_f16 = 0;
499+
swerve_velo_current *= swerve_direction;
500+
if ((swerve_velo_current <= 0) && (swerve_direction == 1)) {
501+
swerve_velo_current = 0;
494502
}
495-
if ((temp_f16 >= 0) && (var_a3 == -1)) {
496-
temp_f16 = 0;
503+
if ((swerve_velo_current >= 0) && (swerve_direction == -1)) {
504+
swerve_velo_current = 0;
497505
}
498-
player->unk_078 += temp_f16 * 0x14;
499-
player->unk_0B8 = var_f0;
500-
player->unk_0B4 = var_v1;
501-
player->unk_0AC = var_a3;
506+
/* unk_078 contributes to rotational velocity (spin). It looks to be set each frame in steering code
507+
(e.g. func_80033AE0), so it does not accumulate values from swerve_velo_current over multiple frames */
508+
player->unk_078 += swerve_velo_current * 20;
509+
player->swerveAccelInit = swerve_accel_init;
510+
player->swerveTimer = swerve_timer;
511+
player->swerveDirection = swerve_direction;
502512
if (player->effects & MIDAIR_EFFECT) {
503-
func_8008C73C(player, playerIndex);
504-
player->kartProps &= ~DRIVING_SPINOUT;
513+
add_spinout_effect(player, playerIndex);
514+
player->kartProps &= ~DRIVING_NEAR_SPINOUT;
505515
}
506516
}
507517

508-
void func_8008D3B0(Player* player, UNUSED s8 playerIndex) {
509-
player->kartProps &= ~DRIVING_SPINOUT;
518+
void remove_driving_near_spinout_effect(Player* player, UNUSED s8 playerIndex) {
519+
player->kartProps &= ~DRIVING_NEAR_SPINOUT;
510520
}
511521

512522
void trigger_shroom(Player* player, s8 playerIndex) {

src/effects.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,15 @@ void clean_effect(Player*, s8);
5050
void func_8008C528(Player*, s8);
5151
void func_8008C62C(Player*, s8);
5252
void func_8008C6D0(Player*, s8);
53-
void func_8008C73C(Player*, s8);
54-
void func_8008C8C4(Player*, s8);
53+
void add_spinout_effect(Player*, s8);
54+
void remove_spinout_effects(Player*, s8);
5555
void func_8008C9EC(Player*, s8);
56-
void func_8008CDC0(Player*, s8);
57-
void func_8008CEB0(Player*, s8);
58-
void func_8008D0E4(Player*, s8);
59-
void func_8008D0FC(Player*, s8);
60-
void func_8008D170(Player*, s8);
61-
void func_8008D3B0(Player*, s8);
56+
void trigger_hit_banana(Player*, s8);
57+
void apply_banana_near_spinout_effect(Player*, s8);
58+
void remove_banana_near_spinout_effect(Player*, s8);
59+
void trigger_driving_spinout(Player*, s8);
60+
void apply_driving_near_spinout_effect(Player*, s8);
61+
void remove_driving_near_spinout_effect(Player*, s8);
6262
void trigger_shroom(Player*, s8);
6363
void apply_mushroom_effect(Player*);
6464
void remove_mushroom_effect(Player*);

0 commit comments

Comments
 (0)