mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-08-19 21:03:42 -07:00
twitch chat as forced navi message
This commit is contained in:
parent
1d20000411
commit
3d2520cf80
3 changed files with 222 additions and 0 deletions
|
@ -282,6 +282,12 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
|||
endif()
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES MSVC_RUNTIME_LIBRARY ${MSVC_RUNTIME_LIBRARY_STR})
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# Find/download curl Libs (For fetching twitch messages)
|
||||
################################################################################
|
||||
find_package(CURL REQUIRED)
|
||||
|
||||
################################################################################
|
||||
# Find/download Dr Libs (For custom audio)
|
||||
################################################################################
|
||||
|
@ -630,6 +636,7 @@ endif()
|
|||
################################################################################
|
||||
# Dependencies
|
||||
################################################################################
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl)
|
||||
add_dependencies(${PROJECT_NAME}
|
||||
libultraship
|
||||
)
|
||||
|
|
|
@ -15,12 +15,86 @@
|
|||
#include "soh/SaveManager.h"
|
||||
#include "soh/ResourceManagerHelpers.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
// #region SOH [NTSC] - Allows custom messages to work on japanese
|
||||
static bool sDisplayNextMessageAsEnglish = false;
|
||||
static u8 sLastLanguage = LANGUAGE_ENG;
|
||||
static u16 sTextBoxNum = 0;
|
||||
// #endregion
|
||||
|
||||
// #region text insert - from https://github.com/Daniel-Uzcategui/OOT/
|
||||
typedef struct {
|
||||
char* originalMessage;
|
||||
char* modifiedMessage;
|
||||
} MessageData;
|
||||
|
||||
struct MemoryStruct {
|
||||
char *memory;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
|
||||
size_t realsize = size * nmemb;
|
||||
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
|
||||
|
||||
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
|
||||
if(!ptr) {
|
||||
/* out of memory! */
|
||||
printf("not enough memory (realloc returned NULL)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mem->memory = ptr;
|
||||
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
||||
mem->size += realsize;
|
||||
mem->memory[mem->size] = 0;
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
char *ModifyMessageThroughAPI(const char *originalMessage) {
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
struct MemoryStruct chunk;
|
||||
|
||||
chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
|
||||
chunk.size = 0; /* no data at this point */
|
||||
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
curl = curl_easy_init();
|
||||
|
||||
if(curl) {
|
||||
struct curl_slist *headers = NULL;
|
||||
|
||||
headers = curl_slist_append(headers, "Content-Type: text/plain");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:5001/twitchMessage");
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, originalMessage);
|
||||
|
||||
/* send all data to this function */
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
|
||||
|
||||
/* we pass our 'chunk' struct to the callback function */
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
if(res != CURLE_OK)
|
||||
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
curl_global_cleanup();
|
||||
|
||||
return chunk.memory;
|
||||
}
|
||||
//endregion
|
||||
|
||||
s16 sTextFade = false; // original name: key_off_flag ?
|
||||
|
||||
u8 D_8014B2F4 = 0;
|
||||
|
@ -366,6 +440,29 @@ void Message_FindMessage(PlayState* play, u16 textId) {
|
|||
font->msgOffset = messageTableEntry->segment;
|
||||
font->msgLength = messageTableEntry->msgSize;
|
||||
|
||||
if (textId == 0x110 || textId == -0x110){ // our hijacked Navi textId
|
||||
// region From Daniel-Uzcategui branch
|
||||
// Dynamically allocate memory for originalMessage
|
||||
char *originalMessage = (char *)malloc((font->msgLength + 1) * sizeof(char));
|
||||
if (originalMessage == NULL) {
|
||||
// Handle error
|
||||
fprintf(stderr, "Memory allocation failed!\n");
|
||||
return;
|
||||
}
|
||||
// Copy the found message to originalMessage
|
||||
strncpy(originalMessage, foundSeg, font->msgLength);
|
||||
originalMessage[font->msgLength] = '\0'; // Null-terminate the string
|
||||
|
||||
// Send the original message to the API and get the modified message
|
||||
char *modifiedMessage = ModifyMessageThroughAPI(originalMessage);
|
||||
|
||||
// Use the modified message instead of the original message
|
||||
font->msgOffset = modifiedMessage;
|
||||
font->msgLength = strlen(modifiedMessage);
|
||||
free(originalMessage);
|
||||
//endregion
|
||||
}
|
||||
|
||||
// "Message found!!!"
|
||||
osSyncPrintf(" メッセージが,見つかった!!! = %x "
|
||||
"(data=%x) (data0=%x) (data1=%x) (data2=%x) (data3=%x)\n",
|
||||
|
|
|
@ -38,6 +38,74 @@
|
|||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
// region - New for checking twitch queue
|
||||
#include <curl/curl.h>
|
||||
|
||||
struct QueueCheckResponse {
|
||||
char* data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static s32 sQueueCheckTimer = 0;
|
||||
static bool sQueueWasEmpty = true;
|
||||
static const s32 QUEUE_CHECK_INTERVAL = 120; // frames
|
||||
|
||||
// write HTTP queue response
|
||||
static size_t WriteQueueCheckCallback(void *contents, size_t size, size_t nmemb, void *userp) {
|
||||
size_t realsize = size * nmemb;
|
||||
struct QueueCheckResponse *response = (struct QueueCheckResponse *)userp;
|
||||
|
||||
char *ptr = realloc(response->data, response->size + realsize + 1);
|
||||
if (!ptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
response->data = ptr;
|
||||
memcpy(&(response->data[response->size]), contents, realsize);
|
||||
response->size += realsize;
|
||||
response->data[response->size] = 0;
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
// check message queue is not empty
|
||||
static bool CheckQueueNotEmpty() {
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
struct QueueCheckResponse response = {0};
|
||||
bool queueNotEmpty = false;
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl) {
|
||||
response.data = malloc(1);
|
||||
response.size = 0;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:5001/queueStatus");
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "");
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0L); // zero length, just need the response
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteQueueCheckCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L); // 1 second timeout
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
if (res == CURLE_OK && response.data) {
|
||||
if (strncmp(response.data, "true", 4) == 0) {
|
||||
queueNotEmpty = true;
|
||||
}
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
if (response.data) {
|
||||
free(response.data);
|
||||
}
|
||||
}
|
||||
|
||||
return queueNotEmpty;
|
||||
}
|
||||
// endregion
|
||||
|
||||
// Some player animations are played at this reduced speed, for reasons yet unclear.
|
||||
// This is called "adjusted" for now.
|
||||
#define PLAYER_ANIM_ADJUSTED_SPEED (2.0f / 3.0f)
|
||||
|
@ -344,6 +412,53 @@ void Player_Action_80850C68(Player* this, PlayState* play);
|
|||
void Player_Action_80850E84(Player* this, PlayState* play);
|
||||
void Player_Action_CsAction(Player* this, PlayState* play);
|
||||
|
||||
void Player_CheckQueueAndSetNavi(Player* this, PlayState* play) {
|
||||
// if we're in normal gameplay
|
||||
if (play->csCtx.state != CS_STATE_IDLE ||
|
||||
this->csAction != 0 ||
|
||||
play->transitionTrigger != TRANS_TRIGGER_OFF ||
|
||||
gSaveContext.health == 0) {
|
||||
return;
|
||||
}
|
||||
if (this->naviActor == NULL) {
|
||||
return; // Navi actor doesn't exist
|
||||
}
|
||||
// Check if Navi is already busy talking
|
||||
if (this->naviActor->flags & ACTOR_FLAG_TALK) {
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure the player is in a state where they can talk to Navi
|
||||
if (this->stateFlags1 & (PLAYER_STATE1_IN_WATER |
|
||||
PLAYER_STATE1_HANGING_OFF_LEDGE |
|
||||
PLAYER_STATE1_INPUT_DISABLED |
|
||||
PLAYER_STATE1_CLIMBING_LEDGE |
|
||||
PLAYER_STATE1_GETTING_ITEM |
|
||||
PLAYER_STATE1_TALKING |
|
||||
PLAYER_STATE1_IN_CUTSCENE |
|
||||
PLAYER_STATE1_CLIMBING_LADDER)) {
|
||||
return; // player is in a state where they can't talk
|
||||
}
|
||||
|
||||
|
||||
sQueueCheckTimer--;
|
||||
|
||||
if (sQueueCheckTimer <= 0) {
|
||||
sQueueCheckTimer = QUEUE_CHECK_INTERVAL;
|
||||
|
||||
bool queueNotEmpty = CheckQueueNotEmpty();
|
||||
|
||||
// queue has messages -> trigger Navi
|
||||
if (queueNotEmpty) {
|
||||
// check if Navi already has a textID ready to go
|
||||
if (this->naviTextId == 0){
|
||||
this->naviTextId = -0x110; // appears to be an unused Navi textID we can hijack
|
||||
// negative ID to force chatting with Navi
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma region[SoH]
|
||||
u8 gWalkSpeedToggle1;
|
||||
u8 gWalkSpeedToggle2;
|
||||
|
@ -11934,6 +12049,9 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) {
|
|||
|
||||
sControlInput = input;
|
||||
|
||||
// for twitch chat checking, regular checks during player main loop
|
||||
Player_CheckQueueAndSetNavi(this, play);
|
||||
|
||||
if (this->unk_A86 < 0) {
|
||||
this->unk_A86++;
|
||||
if (this->unk_A86 == 0) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue