Add rumble when player fires portal gun

This commit is contained in:
James Lambert 2023-10-10 21:43:36 -06:00
parent ed6dcd5948
commit c18354ad76
4 changed files with 227 additions and 28 deletions

View file

@ -3,8 +3,8 @@
#include "defs.h"
#include "util/memory.h"
#include <sched.h>
#include "rumble_pak.h"
#include "../debugger/serial.h"
#include <string.h>
// 0 = disable, 1 = record, 2 = playback
@ -33,7 +33,6 @@ static u8 gControllerReadInProgress = 0;
static u8 gLastControllerQuery = ControllerEventTypeNone;
static u8 gRumblePakState;
static u8 gRumblePakOn;
static u8 gRumbleDelay;
static OSPfs gRumbleBackFs;
@ -85,11 +84,13 @@ void controllersInit(void)
osSetEventMesg(OS_EVENT_SI, &gControllerMsgQ, (OSMesg)&gControllerMessage);
}
int controllerGetTargetRumbleStatus() {
return 0;
void controllerQueryStatus() {
osContStartQuery(&gControllerMsgQ);
gControllerReadInProgress = 1;
gLastControllerQuery = ControllerEventTypeStatus;
}
void controllerHandleMessage() {
int controllerHandleMessage() {
if (gLastControllerQuery == ControllerEventTypeData) {
osContGetReadData(gControllerData);
gControllerReadInProgress = 0;
@ -105,14 +106,10 @@ void controllerHandleMessage() {
}
}
if (gRumblePakState != RumblepakStateInitialized) {
osContStartQuery(&gControllerMsgQ);
gControllerReadInProgress = 1;
gLastControllerQuery = ControllerEventTypeStatus;
}
return TRUE;
} else if (gLastControllerQuery == ControllerEventTypeStatus) {
int prevStatus = gControllerStatus[0].status;
gControllerReadInProgress = 0;
osContGetQuery(&gControllerStatus[0]);
gLastControllerQuery = ControllerEventTypeNone;
@ -120,7 +117,6 @@ void controllerHandleMessage() {
if ((prevStatus != CONT_CARD_ON && gControllerStatus[0].status == CONT_CARD_ON && gRumblePakState == RumblepakStateDisconnected) || gRumblePakState == RumplepakStateUninitialized) {
if (osMotorInit(&gControllerMsgQ, &gRumbleBackFs, 0) == 0) {
gRumblePakState = RumblepakStateInitialized;
gRumbleDelay = 16;
} else {
gRumblePakState = RumblepakStateDisconnected;
gRumblePakOn = 0;
@ -130,37 +126,48 @@ void controllerHandleMessage() {
gRumblePakOn = 0;
}
}
return FALSE;
}
void controllersReadPendingData(void) {
OSMesg msg;
int shouldCheckStatus;
if (osRecvMesg(&gControllerMsgQ, &msg, OS_MESG_NOBLOCK) != -1) {
controllerHandleMessage();
shouldCheckStatus = controllerHandleMessage();
} else {
shouldCheckStatus = FALSE;
}
int targetRumbleStatus = rumblePakCalculateState();
if (gRumblePakState == RumblepakStateInitialized) {
int targetRumbleStatus = controllerGetTargetRumbleStatus();
if (gRumbleDelay > 0) {
--gRumbleDelay;
return;
}
if (targetRumbleStatus != gRumblePakOn) {
shouldCheckStatus = FALSE;
for (int i = 0; i < 3; ++i) {
s32 rumbleError = targetRumbleStatus ? osMotorStart(&gRumbleBackFs) : osMotorStop(&gRumbleBackFs);
if (rumbleError == PFS_ERR_CONTRFAIL) {
gRumblePakState = RumplepakStateUninitialized;
continue;
} else if (rumbleError != 0) {
gRumblePakState = RumblepakStateDisconnected;
gRumblePakOn = 0;
break;
} else {
gRumblePakOn = targetRumbleStatus;
break;
}
}
}
}
if (shouldCheckStatus) {
controllerQueryStatus();
}
}
void controllersSavePreviousState(void) {
for (unsigned i = 0; i < MAX_PLAYERS; ++i) {
gControllerLastDirection[i] = controllerGetDirection(i);

152
src/controls/rumble_pak.c Normal file
View file

@ -0,0 +1,152 @@
#include "rumble_pak.h"
#include <ultra64.h>
#include "../util/time.h"
#define MAX_ACTIVE_RUMBLE 4
struct RumblePakClip gClips[MAX_ACTIVE_RUMBLE];
struct RumblePakClip* gFirstActiveClip = NULL;
struct RumblePakClip* gFirstIdleClip = NULL;
RumbleID gNextRumbleId = 1;
void rumblePakClipInit() {
struct RumblePakClip* prev = NULL;
for (int i = 0; i < MAX_ACTIVE_RUMBLE; ++i) {
struct RumblePakClip* curr = &gClips[i];
if (prev) {
prev->next = curr;
} else {
gFirstIdleClip = curr;
}
curr->wave = NULL;
curr->currentSample = 0;
curr->rumbleId = 0;
}
prev->next = NULL;
}
RumbleID rumblePakClipPlay(struct RumblePakWave* wave) {
if (!gFirstIdleClip) {
return 0;
}
struct RumblePakClip* clip = gFirstIdleClip;
gFirstIdleClip = gFirstIdleClip->next;
clip->next = gFirstActiveClip;
gFirstActiveClip = clip;
clip->currentSample = 0;
clip->wave = wave;
clip->rumbleId = gNextRumbleId;
++gNextRumbleId;
return clip->rumbleId;
}
int rumblePakClipIsActive(RumbleID clip) {
struct RumblePakClip* curr = gFirstActiveClip;
while (curr) {
if (curr->rumbleId == clip) {
return TRUE;
}
curr = curr->next;
}
return FALSE;
}
void rumblePakClipStop(RumbleID clipId) {
struct RumblePakClip* clip = gFirstActiveClip;
while (clip) {
if (clip->rumbleId == clipId) {
break;
}
clip = clip->next;
}
if (!clip) {
return;
}
struct RumblePakClip* curr = gFirstActiveClip;
struct RumblePakClip* prev = NULL;
while (curr) {
if (curr == clip) {
if (prev) {
prev->next = curr->next;
} else {
gFirstActiveClip = curr->next;
}
curr->wave = NULL;
curr->next = NULL;
curr->next = gFirstIdleClip;
gFirstIdleClip = curr;
return;
}
curr = curr->next;
}
}
int gRumbleCurrentBit = 0;
int rumblePakCalculateState() {
int amplitude = 0;
struct RumblePakClip* curr = gFirstActiveClip;
struct RumblePakClip* prev = NULL;
while (curr) {
int sampleAsInt = curr->currentSample >> 6;
if (sampleAsInt >= curr->wave->sampleCount) {
// clip is done, remove it
curr->wave = NULL;
if (prev) {
prev->next = curr->next;
} else {
gFirstActiveClip = curr->next;
}
struct RumblePakClip* next = curr->next;
curr->next = gFirstIdleClip;
gFirstIdleClip = curr;
curr = next;
} else {
int byteIndex = sampleAsInt >> 2;
int byteOffset = sampleAsInt & 0x3;
u8 byte = curr->wave->samples[byteIndex];
amplitude += (byte >> (6 - byteOffset * 2)) & 0x3;
curr->currentSample += curr->wave->samplesPerTick;
prev = curr;
curr = curr->next;
}
}
if (amplitude > 3) {
amplitude = 3;
}
int result = amplitude >= gRumbleCurrentBit;
gRumbleCurrentBit = (gRumbleCurrentBit + 1) & 0x3;
return result;
}

29
src/controls/rumble_pak.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef __CONTROLS__RUMBLE_PAK_H__
#define __CONTROLS__RUMBLE_PAK_H__
struct RumblePakWave {
// 2 bits per sample
unsigned char* samples;
short sampleCount;
// 10.6 fixed point number ticks happens at 60 times per second (50 for pal)
short samplesPerTick;
};
typedef short RumbleID;
struct RumblePakClip {
struct RumblePakWave* wave;
struct RumblePakClip* next;
// 10.6 fixed point number
short currentSample;
RumbleID rumbleId;
};
void rumblePakClipInit();
RumbleID rumblePakClipPlay(struct RumblePakWave* wave);
int rumblePakClipIsActive(RumbleID clip);
void rumblePakClipStop(RumbleID clip);
int rumblePakCalculateState();
#endif

View file

@ -31,6 +31,7 @@
#include "render_plan.h"
#include "../menu/game_menu.h"
#include "../effects/effect_definitions.h"
#include "../controls/rumble_pak.h"
extern struct GameMenu gGameMenu;
@ -321,7 +322,15 @@ void sceneRender(struct Scene* scene, struct RenderState* renderState, struct Gr
}
u8 gFireGunRumbleWaveData[] = {
0xFF, 0xE9,
};
struct RumblePakWave gFireGunRumbleWave = {
.samples = gFireGunRumbleWaveData,
.sampleCount = 8,
.samplesPerTick = 1 << 6,
};
void sceneCheckPortals(struct Scene* scene) {
if (playerIsDead(&scene->player)) {
@ -352,6 +361,7 @@ void sceneCheckPortals(struct Scene* scene) {
scene->player.flags |= PlayerJustShotPortalGun;
hudPortalFired(&scene->hud, 0);
soundPlayerPlay(soundsPortalgunShoot[0], 1.0f, 1.0f, NULL, NULL);
rumblePakClipPlay(&gFireGunRumbleWave);
}
if (((fireBlue && !fireOrange) || (!hasOrange && fireOrange)) && hasBlue && !playerIsGrabbing(&scene->player) && !portalGunIsFiring(&scene->portalGun)) {
@ -359,6 +369,7 @@ void sceneCheckPortals(struct Scene* scene) {
scene->player.flags |= PlayerJustShotPortalGun;
hudPortalFired(&scene->hud, 1);
soundPlayerPlay(soundsPortalgunShoot[1], 1.0f, 1.0f, NULL, NULL);
rumblePakClipPlay(&gFireGunRumbleWave);
}
if ((fireOrange || fireBlue) && playerIsGrabbing(&scene->player)){