mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-08-26 16:15:19 -07:00
Reintegrate accessibility using game-interactor.
This commit is contained in:
parent
70da2c9763
commit
1a07a3e810
11 changed files with 140 additions and 18 deletions
|
@ -16,6 +16,11 @@
|
|||
#include <unordered_set>
|
||||
#include "soh/Enhancements/speechsynthesizer/SpeechSynthesizer.h"
|
||||
#include "soh/Enhancements/tts/tts.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
extern "C" {
|
||||
extern PlayState* gPlayState;
|
||||
extern bool freezeGame;
|
||||
}
|
||||
|
||||
const char* GetLanguageCode();
|
||||
|
||||
|
@ -50,7 +55,7 @@ typedef struct {
|
|||
class ActorAccessibility {
|
||||
public:
|
||||
bool isOn = false;
|
||||
uint64_t nextActorID;
|
||||
uint64_t nextActorID = 0;
|
||||
SupportedActors_t supportedActors;
|
||||
TrackedActors_t trackedActors;
|
||||
AccessibleActorList_t accessibleActorList;
|
||||
|
@ -59,7 +64,7 @@ class ActorAccessibility {
|
|||
AccessibleAudioEngine* audioEngine;
|
||||
SfxExtractor sfxExtractor;
|
||||
std::unordered_map<s16, SfxRecord> sfxMap;//Maps internal sfx to external (prerendered) resources.
|
||||
|
||||
int extractSfx = 0;
|
||||
};
|
||||
static ActorAccessibility* aa;
|
||||
|
||||
|
@ -68,12 +73,45 @@ uint64_t ActorAccessibility_GetNextID() {
|
|||
aa->nextActorID++;
|
||||
return result;
|
||||
}
|
||||
void ActorAccessibility_Init() {
|
||||
aa = new ActorAccessibility();
|
||||
ActorAccessibility_InitAudio();
|
||||
ActorAccessibility_InitActors();
|
||||
|
||||
// Hooks for game-interactor.
|
||||
void ActorAccessibility_OnActorInit(void* actor) {
|
||||
ActorAccessibility_TrackNewActor((Actor*)actor);
|
||||
}
|
||||
void ActorAccessibility_OnGameFrameUpdate() {
|
||||
if (gPlayState == NULL)
|
||||
return;
|
||||
|
||||
ActorAccessibility_RunAccessibilityForAllActors(gPlayState);
|
||||
}
|
||||
void ActorAccessibility_OnActorDestroy(void* actor)
|
||||
{
|
||||
ActorAccessibility_RemoveTrackedActor((Actor*) actor);
|
||||
|
||||
}
|
||||
void ActorAccessibility_OnGameStillFrozen()
|
||||
{
|
||||
if (gPlayState == NULL)
|
||||
return;
|
||||
if (aa->extractSfx)
|
||||
ActorAccessibility_HandleSoundExtractionMode(gPlayState);
|
||||
|
||||
}
|
||||
void ActorAccessibility_Init() {
|
||||
|
||||
aa = new ActorAccessibility();
|
||||
aa->extractSfx = CVarGetInteger("gExtractSfx", 0);
|
||||
if (aa->extractSfx)
|
||||
freezeGame = true;
|
||||
ActorAccessibility_InitAudio();
|
||||
ActorAccessibility_InitActors();
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(ActorAccessibility_OnActorInit);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorDestroy>(ActorAccessibility_OnActorDestroy);
|
||||
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(ActorAccessibility_OnGameFrameUpdate);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameStillFrozen>(ActorAccessibility_OnGameStillFrozen);
|
||||
|
||||
}
|
||||
void ActorAccessibility_Shutdown() {
|
||||
ActorAccessibility_ShutdownAudio();
|
||||
delete aa;
|
||||
|
@ -114,7 +152,7 @@ int ActorAccessibility_GetRandomStartingFrameCount(int min, int max) {
|
|||
|
||||
}
|
||||
|
||||
void ActorAccessibility_TrackNewActor(Actor* actor) {
|
||||
void ActorAccessibility_TrackNewActor(Actor * actor) {
|
||||
// Don't track actors for which no accessibility policy has been configured.
|
||||
ActorAccessibilityPolicy* policy = ActorAccessibility_GetPolicyForActor(actor->id);
|
||||
if (policy == NULL)
|
||||
|
@ -525,5 +563,3 @@ return NULL;//Resource doesn't exist, user's gotta run the extractor.
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -14,9 +14,10 @@ extern f32 D_801333E0;
|
|||
extern s8 D_801333E8;
|
||||
extern u8 D_801333F0;
|
||||
void AudioMgr_CreateNextAudioBuffer(s16* samples, u32 num_samples);
|
||||
extern bool freezeGame;
|
||||
}
|
||||
enum {
|
||||
STEP_SETUP,
|
||||
STEP_SETUP = 0,
|
||||
STEP_MAIN,
|
||||
STEP_FINISHED,
|
||||
STEP_ERROR,
|
||||
|
@ -92,6 +93,9 @@ void SfxExtractor::renderOutput() {
|
|||
}
|
||||
void SfxExtractor::setup() {
|
||||
try {
|
||||
ogMusicVolume = CVarGetFloat("gMainMusicVolume", 1.0);
|
||||
CVarSetFloat("gMainMusicVolume", 0.0);
|
||||
|
||||
SpeechSynthesizer::Instance->Speak("Sfx extraction speedrun initiated. Please wait. This will take a few minutes.", GetLanguageCode());
|
||||
// Kill the audio thread so we can take control.
|
||||
captureThreadState = CT_WAITING;
|
||||
|
@ -167,6 +171,8 @@ void SfxExtractor::finished() {
|
|||
SpeechSynthesizer::Instance->Speak(ss.str().c_str(), GetLanguageCode());
|
||||
} else
|
||||
Audio_PlayFanfare(NA_BGM_ITEM_GET);
|
||||
freezeGame = false;
|
||||
|
||||
}
|
||||
void SfxExtractor::maybeGiveProgressReport() {
|
||||
size_t ripsRemaining = sfxToRip.size() + 1;
|
||||
|
@ -182,7 +188,8 @@ void SfxExtractor::maybeGiveProgressReport() {
|
|||
SfxExtractor::SfxExtractor() {
|
||||
currentStep = STEP_SETUP;
|
||||
}
|
||||
void SfxExtractor::frameCallback() {
|
||||
|
||||
void SfxExtractor::frameCallback() {
|
||||
switch (currentStep) {
|
||||
case STEP_SETUP:
|
||||
setup();
|
||||
|
|
|
@ -10,6 +10,7 @@ class SfxExtractor {
|
|||
s16 currentSfx;
|
||||
std::vector<int16_t> tempStorage; // Stores raw audio data for the sfx currently being ripped.
|
||||
int16_t* tempBuffer; // Raw pointer to the above vector.
|
||||
f32 ogMusicVolume;
|
||||
int progressMilestones[9]; // Implements progress reports after every 10 percent.
|
||||
// Check if a buffer contains meaningful audio output.
|
||||
bool isAllZero(int16_t* buffer, size_t count);
|
||||
|
@ -23,6 +24,7 @@ class SfxExtractor {
|
|||
void maybeGiveProgressReport();
|
||||
public:
|
||||
SfxExtractor();
|
||||
|
||||
void frameCallback();
|
||||
void prime();
|
||||
// The below is called by the (hijacked) audio thread.
|
||||
|
|
|
@ -153,6 +153,8 @@ public:
|
|||
DEFINE_HOOK(OnOcarinaSongAction, void());
|
||||
DEFINE_HOOK(OnActorInit, void(void* actor));
|
||||
DEFINE_HOOK(OnActorUpdate, void(void* actor));
|
||||
DEFINE_HOOK(OnActorDestroy, void(void* actor));
|
||||
|
||||
DEFINE_HOOK(OnPlayerBonk, void());
|
||||
DEFINE_HOOK(OnPlayDestroy, void());
|
||||
DEFINE_HOOK(OnPlayDrawEnd, void());
|
||||
|
@ -180,6 +182,7 @@ public:
|
|||
DEFINE_HOOK(OnUpdateFileNameSelection, void(int16_t charCode));
|
||||
|
||||
DEFINE_HOOK(OnSetGameLanguage, void());
|
||||
DEFINE_HOOK(OnGameStillFrozen, void());
|
||||
|
||||
// Helpers
|
||||
static bool IsSaveLoaded();
|
||||
|
|
|
@ -49,8 +49,11 @@ void GameInteractor_ExecuteOnActorInit(void* actor) {
|
|||
void GameInteractor_ExecuteOnActorUpdate(void* actor) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnActorUpdate>(actor);
|
||||
}
|
||||
void GameInteractor_ExecuteOnActorDestroy(void* actor) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnActorDestroy>(actor);
|
||||
}
|
||||
|
||||
void GameInteractor_ExecuteOnPlayerBonk() {
|
||||
void GameInteractor_ExecuteOnPlayerBonk() {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerBonk>();
|
||||
}
|
||||
|
||||
|
@ -149,3 +152,7 @@ void GameInteractor_ExecuteOnUpdateFileNameSelection(int16_t charCode) {
|
|||
void GameInteractor_ExecuteOnSetGameLanguage() {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetGameLanguage>();
|
||||
}
|
||||
void GameInteractor_ExecuteOnGameStillFrozen()
|
||||
{
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnGameStillFrozen>();
|
||||
}
|
|
@ -15,7 +15,10 @@ void GameInteractor_ExecuteOnSceneSpawnActors();
|
|||
void GameInteractor_ExecuteOnPlayerUpdate();
|
||||
void GameInteractor_ExecuteOnOcarinaSongAction();
|
||||
void GameInteractor_ExecuteOnActorInit(void* actor);
|
||||
void GameInteractor_ExecuteOnActorInit(void* actor);
|
||||
void GameInteractor_ExecuteOnActorUpdate(void* actor);
|
||||
void GameInteractor_ExecuteOnActorDestroy(void* actor);
|
||||
|
||||
void GameInteractor_ExecuteOnPlayerBonk();
|
||||
void GameInteractor_ExecuteOnOcarinaSongAction();
|
||||
void GameInteractor_ExecuteOnPlayDestroy();
|
||||
|
@ -48,6 +51,7 @@ void GameInteractor_ExecuteOnUpdateFileNameSelection(int16_t charCode);
|
|||
|
||||
// MARK: - Game
|
||||
void GameInteractor_ExecuteOnSetGameLanguage();
|
||||
void GameInteractor_ExecuteOnGameStillFrozen();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -112,7 +112,8 @@ CrowdControl* CrowdControl::Instance;
|
|||
#include "soh/resource/importer/BackgroundFactory.h"
|
||||
|
||||
#include "soh/config/ConfigUpdaters.h"
|
||||
|
||||
#include "soh/Enhancements/accessible-actors/ActorAccessibility.h"
|
||||
#include "Enhancements//accessible-actors/ActorAccessibility.h"
|
||||
OTRGlobals* OTRGlobals::Instance;
|
||||
SaveManager* SaveManager::Instance;
|
||||
CustomMessageManager* CustomMessageManager::Instance;
|
||||
|
@ -230,6 +231,11 @@ OTRGlobals::OTRGlobals() {
|
|||
}
|
||||
}
|
||||
}
|
||||
std::string sohAccessibilityPath = LUS::Context::GetPathRelativeToAppDirectory("accessibility.otr");
|
||||
if (std::filesystem::exists(sohAccessibilityPath)) {
|
||||
OTRFiles.push_back(sohAccessibilityPath);
|
||||
}
|
||||
|
||||
std::unordered_set<uint32_t> ValidHashes = {
|
||||
OOT_PAL_MQ,
|
||||
OOT_NTSC_JP_MQ,
|
||||
|
@ -395,9 +401,13 @@ void OTRAudio_Thread() {
|
|||
// 3 is the maximum authentic frame divisor.
|
||||
s16 audio_buffer[SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3];
|
||||
for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) {
|
||||
AudioMgr_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), num_audio_samples);
|
||||
}
|
||||
AudioMgr_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS),
|
||||
num_audio_samples);
|
||||
// Give accessibility a chance to merge its own audio in.
|
||||
ActorAccessibility_MixAccessibleAudioWithGameAudio(
|
||||
audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), num_audio_samples);
|
||||
|
||||
}
|
||||
AudioPlayer_Play((u8*)audio_buffer, num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE));
|
||||
|
||||
audio.processing = false;
|
||||
|
@ -774,6 +784,7 @@ extern "C" void InitOTR() {
|
|||
|
||||
clearMtx = (uintptr_t)&gMtxClear;
|
||||
OTRMessage_Init();
|
||||
ActorAccessibility_Init();
|
||||
OTRAudio_Init();
|
||||
OTRExtScanner();
|
||||
VanillaItemTable_Init();
|
||||
|
@ -817,7 +828,7 @@ extern "C" void DeinitOTR() {
|
|||
CrowdControl::Instance->Disable();
|
||||
CrowdControl::Instance->Shutdown();
|
||||
#endif
|
||||
|
||||
ActorAccessibility_Shutdown();
|
||||
// Destroying gui here because we have shared ptrs to LUS objects which output to SPDLOG which is destroyed before these shared ptrs.
|
||||
SohGui::Destroy();
|
||||
|
||||
|
@ -1017,7 +1028,7 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
|
|||
last_update_rate = R_UPDATE_RATE;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> Lock(audio.mutex);
|
||||
std::unique_lock<std::mutex> Lock(audio.mutex);
|
||||
while (audio.processing) {
|
||||
audio.cv_from_thread.wait(Lock);
|
||||
}
|
||||
|
@ -2190,3 +2201,39 @@ extern "C" void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex) {
|
|||
extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement) {
|
||||
gfx_register_blended_texture(name, mask, replacement);
|
||||
}
|
||||
|
||||
void OTRAudio_SfxCaptureThread() {
|
||||
while (audio.running) {
|
||||
{
|
||||
std::unique_lock<std::mutex> Lock(audio.mutex);
|
||||
while (!audio.processing && audio.running) {
|
||||
audio.cv_to_thread.wait(Lock);
|
||||
}
|
||||
|
||||
if (!audio.running) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::unique_lock<std::mutex> Lock(audio.mutex);
|
||||
ActorAccessibility_DoSoundExtractionStep();
|
||||
audio.processing = false;
|
||||
audio.cv_from_thread.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void OTRAudio_InstallSfxCaptureThread() {
|
||||
OTRAudio_Exit();
|
||||
audio.running = true;
|
||||
audio.thread = std::thread(OTRAudio_SfxCaptureThread);
|
||||
|
||||
}
|
||||
extern "C" void OTRAudio_UninstallSfxCaptureThread()
|
||||
{
|
||||
OTRAudio_Exit();
|
||||
audio.running = true;
|
||||
audio.thread = std::thread(OTRAudio_Thread);
|
||||
}
|
||||
std::unique_lock<std::mutex> OTRAudio_Lock()
|
||||
{
|
||||
return std::unique_lock<std::mutex>(audio.mutex);
|
||||
}
|
|
@ -156,14 +156,19 @@ void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
|
|||
void SaveManager_ThreadPoolWait();
|
||||
|
||||
int32_t GetGIID(uint32_t itemID);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
uint64_t GetUnixTimestamp();
|
||||
void OTRAudio_InstallSfxCaptureThread();
|
||||
void OTRAudio_UninstallSfxCaptureThread();
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
std::unique_lock<std::mutex> OTRAudio_Lock();
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "soh/mixer.h"
|
||||
|
||||
#include "soh/Enhancements/audio/AudioEditor.h"
|
||||
extern bool freezeGame;
|
||||
|
||||
typedef struct {
|
||||
u8 unk_0;
|
||||
|
@ -371,6 +372,9 @@ extern f32 D_80130F28;
|
|||
|
||||
void Audio_QueueSeqCmd(u32 cmd)
|
||||
{
|
||||
if (freezeGame)
|
||||
return;//No music during SFX rip.
|
||||
|
||||
u8 op = cmd >> 28;
|
||||
if (op == 0 || op == 2 || op == 12) {
|
||||
u8 seqId = cmd & 0xFF;
|
||||
|
|
|
@ -1224,6 +1224,7 @@ void Actor_Init(Actor* actor, PlayState* play) {
|
|||
}
|
||||
|
||||
void Actor_Destroy(Actor* actor, PlayState* play) {
|
||||
GameInteractor_ExecuteOnActorDestroy(actor);
|
||||
if (actor->destroy != NULL) {
|
||||
actor->destroy(actor, play);
|
||||
actor->destroy = NULL;
|
||||
|
@ -2476,6 +2477,7 @@ u32 D_80116068[ACTORCAT_MAX] = {
|
|||
};
|
||||
|
||||
void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) {
|
||||
|
||||
Actor* refActor;
|
||||
Actor* actor;
|
||||
Player* player;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
bool freezeGame = false;//Used for SFX ripper.
|
||||
void* D_8012D1F0 = NULL;
|
||||
//UNK_TYPE D_8012D1F4 = 0; // unused
|
||||
Input* D_8012D1F8 = NULL;
|
||||
|
@ -753,6 +753,11 @@ void Play_Update(PlayState* play) {
|
|||
Input* input;
|
||||
u32 i;
|
||||
s32 pad2;
|
||||
if (freezeGame) {
|
||||
GameInteractor_ExecuteOnGameStillFrozen();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
input = play->state.input;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue