Daeth link done
This commit is contained in:
@@ -176,7 +176,6 @@ typedef struct ArchipelagoLocationData {
|
|||||||
typedef struct ShipArchipelagoSaveContextData {
|
typedef struct ShipArchipelagoSaveContextData {
|
||||||
u8 isArchipelago;
|
u8 isArchipelago;
|
||||||
u32 lastReceivedItemIndex;
|
u32 lastReceivedItemIndex;
|
||||||
u8 deathLink;
|
|
||||||
char roomHash[100];
|
char roomHash[100];
|
||||||
char slotName[17];
|
char slotName[17];
|
||||||
ArchipelagoLocationData locations[RC_MAX];
|
ArchipelagoLocationData locations[RC_MAX];
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ DEFINE_HOOK(OnFlagSet, (int16_t flagType, int16_t flag));
|
|||||||
DEFINE_HOOK(OnFlagUnset, (int16_t flagType, int16_t flag));
|
DEFINE_HOOK(OnFlagUnset, (int16_t flagType, int16_t flag));
|
||||||
DEFINE_HOOK(OnSceneSpawnActors, ());
|
DEFINE_HOOK(OnSceneSpawnActors, ());
|
||||||
DEFINE_HOOK(OnPlayerUpdate, ());
|
DEFINE_HOOK(OnPlayerUpdate, ());
|
||||||
|
DEFINE_HOOK(OnPlayerDeath, ());
|
||||||
DEFINE_HOOK(OnSetDoAction, (uint16_t action));
|
DEFINE_HOOK(OnSetDoAction, (uint16_t action));
|
||||||
DEFINE_HOOK(OnOcarinaSongAction, ());
|
DEFINE_HOOK(OnOcarinaSongAction, ());
|
||||||
DEFINE_HOOK(OnCuccoOrChickenHatch, ());
|
DEFINE_HOOK(OnCuccoOrChickenHatch, ());
|
||||||
|
|||||||
@@ -89,6 +89,10 @@ void GameInteractor_ExecuteOnPlayerUpdate() {
|
|||||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerUpdate>();
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerUpdate>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameInteractor_ExecuteOnPlayerDeath() {
|
||||||
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerDeath>();
|
||||||
|
}
|
||||||
|
|
||||||
void GameInteractor_ExecuteOnSetDoAction(uint16_t action) {
|
void GameInteractor_ExecuteOnSetDoAction(uint16_t action) {
|
||||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetDoAction>(action);
|
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetDoAction>(action);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ void GameInteractor_ExecuteOnFlagSet(int16_t flagType, int16_t flag);
|
|||||||
void GameInteractor_ExecuteOnFlagUnset(int16_t flagType, int16_t flag);
|
void GameInteractor_ExecuteOnFlagUnset(int16_t flagType, int16_t flag);
|
||||||
void GameInteractor_ExecuteOnSceneSpawnActors();
|
void GameInteractor_ExecuteOnSceneSpawnActors();
|
||||||
void GameInteractor_ExecuteOnPlayerUpdate();
|
void GameInteractor_ExecuteOnPlayerUpdate();
|
||||||
|
void GameInteractor_ExecuteOnPlayerDeath();
|
||||||
void GameInteractor_ExecuteOnSetDoAction(uint16_t action);
|
void GameInteractor_ExecuteOnSetDoAction(uint16_t action);
|
||||||
void GameInteractor_ExecuteOnOcarinaSongAction();
|
void GameInteractor_ExecuteOnOcarinaSongAction();
|
||||||
void GameInteractor_ExecuteOnCuccoOrChickenHatch();
|
void GameInteractor_ExecuteOnCuccoOrChickenHatch();
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ ArchipelagoClient::ArchipelagoClient() {
|
|||||||
gameWon = false;
|
gameWon = false;
|
||||||
itemQueued = false;
|
itemQueued = false;
|
||||||
disconnecting = false;
|
disconnecting = false;
|
||||||
|
isDeathLinkedDeath = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchipelagoClient& ArchipelagoClient::GetInstance() {
|
ArchipelagoClient& ArchipelagoClient::GetInstance() {
|
||||||
@@ -63,7 +64,9 @@ bool ArchipelagoClient::StartClient() {
|
|||||||
|
|
||||||
apClient->set_room_info_handler([&]() {
|
apClient->set_room_info_handler([&]() {
|
||||||
std::list<std::string> tags;
|
std::list<std::string> tags;
|
||||||
// tags.push_back("DeathLink"); // todo, implement deathlink
|
if (CVarGetInteger(CVAR_REMOTE_ARCHIPELAGO("DeathLink"), 0)) {
|
||||||
|
tags.push_back("DeathLink");
|
||||||
|
}
|
||||||
apClient->ConnectSlot(CVarGetString(CVAR_REMOTE_ARCHIPELAGO("SlotName"), ""),
|
apClient->ConnectSlot(CVarGetString(CVAR_REMOTE_ARCHIPELAGO("SlotName"), ""),
|
||||||
CVarGetString(CVAR_REMOTE_ARCHIPELAGO("Password"), ""), 0b001, tags);
|
CVarGetString(CVAR_REMOTE_ARCHIPELAGO("Password"), ""), 0b001, tags);
|
||||||
});
|
});
|
||||||
@@ -208,6 +211,25 @@ bool ArchipelagoClient::StartClient() {
|
|||||||
ArchipelagoConsole_PrintJson(coloredNodes);
|
ArchipelagoConsole_PrintJson(coloredNodes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
apClient->set_bounced_handler([&](const nlohmann::json data) {
|
||||||
|
std::list<std::string> tags = data["tags"];
|
||||||
|
bool deathLink = (std::find(tags.begin(), tags.end(), "DeathLink") != tags.end());
|
||||||
|
|
||||||
|
if (deathLink && data["data"]["source"] != apClient->get_slot()) {
|
||||||
|
if (GameInteractor::IsSaveLoaded()) {
|
||||||
|
gSaveContext.health = 0;
|
||||||
|
Notification::Emit({ .prefix = data["data"]["source"],
|
||||||
|
.message = "died. Cause:",
|
||||||
|
.suffix = data["data"]["cause"] });
|
||||||
|
std::string deathLinkMessage =
|
||||||
|
"[LOG] Received death link from " + std::string(data["data"]["source"]) + ". Cause: " + std::string(data["data"]["cause"]);
|
||||||
|
ArchipelagoConsole_SendMessage(deathLinkMessage.c_str());
|
||||||
|
|
||||||
|
isDeathLinkedDeath = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,11 +448,67 @@ uint8_t ArchipelagoClient::GetConnectionStatus() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ArchipelagoClient::OnItemGiven(uint32_t rc, GetItemEntry gi, uint8_t isGiSkipped) {
|
||||||
|
if (rc == RC_ARCHIPELAGO_RECEIVED_ITEM) {
|
||||||
|
gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex++;
|
||||||
|
ArchipelagoClient::GetInstance().itemQueued = false;
|
||||||
|
} else {
|
||||||
|
ArchipelagoClient::GetInstance().CheckLocation((RandomizerCheck)rc);
|
||||||
|
|
||||||
|
if (isGiSkipped && gi.modIndex == MOD_RANDOMIZER &&
|
||||||
|
(gi.getItemId == RG_ARCHIPELAGO_ITEM_PROGRESSIVE || gi.getItemId == RG_ARCHIPELAGO_ITEM_USEFUL ||
|
||||||
|
gi.getItemId == RG_ARCHIPELAGO_ITEM_JUNK)) {
|
||||||
|
|
||||||
|
const char* itemIcon = "";
|
||||||
|
switch (gi.getItemId) {
|
||||||
|
case RG_ARCHIPELAGO_ITEM_PROGRESSIVE:
|
||||||
|
itemIcon = "Archipelago Progressive Icon";
|
||||||
|
break;
|
||||||
|
case RG_ARCHIPELAGO_ITEM_USEFUL:
|
||||||
|
itemIcon = "Archipelago Useful Icon";
|
||||||
|
break;
|
||||||
|
case RG_ARCHIPELAGO_ITEM_JUNK:
|
||||||
|
itemIcon = "Archipelago Junk Icon";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification::Emit(
|
||||||
|
{ .itemIcon = itemIcon,
|
||||||
|
.prefix = std::string(gSaveContext.ship.quest.data.archipelago.locations[rc].itemName),
|
||||||
|
.message = " for ",
|
||||||
|
.suffix = std::string(gSaveContext.ship.quest.data.archipelago.locations[rc].playerName) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArchipelagoClient::SendDeathLink() {
|
||||||
|
if (apClient && CVarGetInteger(CVAR_REMOTE_ARCHIPELAGO("DeathLink"), 0) && !isDeathLinkedDeath) {
|
||||||
|
nlohmann::json data{
|
||||||
|
{ "time", apClient->get_server_time() },
|
||||||
|
{ "cause", "Shipwrecked by King Harkinian." },
|
||||||
|
{ "source", apClient->get_slot() }
|
||||||
|
};
|
||||||
|
apClient->Bounce(data, {}, {}, { "DeathLink" });
|
||||||
|
|
||||||
|
Notification::Emit({ .message = "Sending Death Link" });
|
||||||
|
ArchipelagoConsole_SendMessage("[LOG] Died, sending death link.");
|
||||||
|
}
|
||||||
|
|
||||||
|
isDeathLinkedDeath = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArchipelagoClient::SetDeathLinkTag() {
|
||||||
|
std::list<std::string> tags;
|
||||||
|
if (CVarGetInteger(CVAR_REMOTE_ARCHIPELAGO("DeathLink"), 0)) {
|
||||||
|
tags.push_back("DeathLink");
|
||||||
|
}
|
||||||
|
apClient->ConnectUpdate(false, 1, true, tags);
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" void Archipelago_InitSaveFile() {
|
extern "C" void Archipelago_InitSaveFile() {
|
||||||
gSaveContext.ship.quest.data.archipelago.isArchipelago = 1;
|
gSaveContext.ship.quest.data.archipelago.isArchipelago = 1;
|
||||||
|
|
||||||
nlohmann::json slotData = ArchipelagoClient::GetInstance().GetSlotData();
|
nlohmann::json slotData = ArchipelagoClient::GetInstance().GetSlotData();
|
||||||
gSaveContext.ship.quest.data.archipelago.deathLink = slotData["death_link"];
|
|
||||||
|
|
||||||
std::vector<ArchipelagoClient::ApItem> scoutedItems = ArchipelagoClient::GetInstance().GetScoutedItems();
|
std::vector<ArchipelagoClient::ApItem> scoutedItems = ArchipelagoClient::GetInstance().GetScoutedItems();
|
||||||
|
|
||||||
@@ -456,7 +534,6 @@ void LoadArchipelagoData() {
|
|||||||
SaveManager::Instance->LoadData("isArchipelago", gSaveContext.ship.quest.data.archipelago.isArchipelago);
|
SaveManager::Instance->LoadData("isArchipelago", gSaveContext.ship.quest.data.archipelago.isArchipelago);
|
||||||
SaveManager::Instance->LoadData("lastReceivedItemIndex",
|
SaveManager::Instance->LoadData("lastReceivedItemIndex",
|
||||||
gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex);
|
gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex);
|
||||||
SaveManager::Instance->LoadData("deathLink", gSaveContext.ship.quest.data.archipelago.deathLink);
|
|
||||||
|
|
||||||
SaveManager::Instance->LoadCharArray("roomHash", gSaveContext.ship.quest.data.archipelago.roomHash,
|
SaveManager::Instance->LoadCharArray("roomHash", gSaveContext.ship.quest.data.archipelago.roomHash,
|
||||||
ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.roomHash));
|
ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.roomHash));
|
||||||
@@ -480,7 +557,6 @@ void SaveArchipelagoData(SaveContext* saveContext, int sectionID, bool fullSave)
|
|||||||
SaveManager::Instance->SaveData("isArchipelago", saveContext->ship.quest.data.archipelago.isArchipelago);
|
SaveManager::Instance->SaveData("isArchipelago", saveContext->ship.quest.data.archipelago.isArchipelago);
|
||||||
SaveManager::Instance->SaveData("lastReceivedItemIndex",
|
SaveManager::Instance->SaveData("lastReceivedItemIndex",
|
||||||
saveContext->ship.quest.data.archipelago.lastReceivedItemIndex);
|
saveContext->ship.quest.data.archipelago.lastReceivedItemIndex);
|
||||||
SaveManager::Instance->SaveData("deathLink", saveContext->ship.quest.data.archipelago.deathLink);
|
|
||||||
|
|
||||||
SaveManager::Instance->SaveData("roomHash", saveContext->ship.quest.data.archipelago.roomHash);
|
SaveManager::Instance->SaveData("roomHash", saveContext->ship.quest.data.archipelago.roomHash);
|
||||||
SaveManager::Instance->SaveData("slotName", saveContext->ship.quest.data.archipelago.slotName);
|
SaveManager::Instance->SaveData("slotName", saveContext->ship.quest.data.archipelago.slotName);
|
||||||
@@ -499,7 +575,6 @@ void SaveArchipelagoData(SaveContext* saveContext, int sectionID, bool fullSave)
|
|||||||
void InitArchipelagoData(bool isDebug) {
|
void InitArchipelagoData(bool isDebug) {
|
||||||
gSaveContext.ship.quest.data.archipelago.isArchipelago = 0;
|
gSaveContext.ship.quest.data.archipelago.isArchipelago = 0;
|
||||||
gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex = 0;
|
gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex = 0;
|
||||||
gSaveContext.ship.quest.data.archipelago.deathLink = 0;
|
|
||||||
|
|
||||||
SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.roomHash, "",
|
SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.roomHash, "",
|
||||||
ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.roomHash));
|
ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.roomHash));
|
||||||
@@ -519,42 +594,18 @@ void RegisterArchipelago() {
|
|||||||
ArchipelagoClient::GetInstance();
|
ArchipelagoClient::GetInstance();
|
||||||
|
|
||||||
COND_HOOK(GameInteractor::OnGameFrameUpdate, true, []() { ArchipelagoClient::GetInstance().Poll(); });
|
COND_HOOK(GameInteractor::OnGameFrameUpdate, true, []() { ArchipelagoClient::GetInstance().Poll(); });
|
||||||
|
|
||||||
COND_HOOK(GameInteractor::PostLoadGame, true,
|
COND_HOOK(GameInteractor::PostLoadGame, true,
|
||||||
[](int32_t file_id) { ArchipelagoClient::GetInstance().GameLoaded(); });
|
[](int32_t file_id) { ArchipelagoClient::GetInstance().GameLoaded(); });
|
||||||
|
|
||||||
COND_HOOK(
|
COND_HOOK(
|
||||||
GameInteractor::OnRandomizerItemGivenHooks, IS_ARCHIPELAGO,
|
GameInteractor::OnRandomizerItemGivenHooks, IS_ARCHIPELAGO,
|
||||||
[](uint32_t rc, GetItemEntry gi, uint8_t isGiSkipped) {
|
[](uint32_t rc, GetItemEntry gi, uint8_t isGiSkipped) {
|
||||||
if (rc == RC_ARCHIPELAGO_RECEIVED_ITEM) {
|
ArchipelagoClient::GetInstance().OnItemGiven(rc, gi, isGiSkipped);
|
||||||
gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex++;
|
|
||||||
ArchipelagoClient::GetInstance().itemQueued = false;
|
|
||||||
} else {
|
|
||||||
ArchipelagoClient::GetInstance().CheckLocation((RandomizerCheck)rc);
|
|
||||||
|
|
||||||
if (isGiSkipped && gi.modIndex == MOD_RANDOMIZER &&
|
|
||||||
(gi.getItemId == RG_ARCHIPELAGO_ITEM_PROGRESSIVE || gi.getItemId == RG_ARCHIPELAGO_ITEM_USEFUL ||
|
|
||||||
gi.getItemId == RG_ARCHIPELAGO_ITEM_JUNK)) {
|
|
||||||
|
|
||||||
const char* itemIcon = "";
|
|
||||||
switch (gi.getItemId) {
|
|
||||||
case RG_ARCHIPELAGO_ITEM_PROGRESSIVE:
|
|
||||||
itemIcon = "Archipelago Progressive Icon";
|
|
||||||
break;
|
|
||||||
case RG_ARCHIPELAGO_ITEM_USEFUL:
|
|
||||||
itemIcon = "Archipelago Useful Icon";
|
|
||||||
break;
|
|
||||||
case RG_ARCHIPELAGO_ITEM_JUNK:
|
|
||||||
itemIcon = "Archipelago Junk Icon";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Notification::Emit(
|
|
||||||
{ .itemIcon = itemIcon,
|
|
||||||
.prefix = std::string(gSaveContext.ship.quest.data.archipelago.locations[rc].itemName),
|
|
||||||
.message = " for ",
|
|
||||||
.suffix = std::string(gSaveContext.ship.quest.data.archipelago.locations[rc].playerName) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
COND_HOOK(GameInteractor::OnPlayerDeath, IS_ARCHIPELAGO,
|
||||||
|
[]() { ArchipelagoClient::GetInstance().SendDeathLink(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterShipInitFunc initFunc(RegisterArchipelago, { "IS_ARCHIPELAGO" });
|
static RegisterShipInitFunc initFunc(RegisterArchipelago, { "IS_ARCHIPELAGO" });
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ class ArchipelagoClient {
|
|||||||
const std::string GetSlotName() const;
|
const std::string GetSlotName() const;
|
||||||
|
|
||||||
uint8_t GetConnectionStatus();
|
uint8_t GetConnectionStatus();
|
||||||
|
void OnItemGiven(uint32_t rc, GetItemEntry gi, uint8_t isGiSkipped);
|
||||||
|
void SendDeathLink();
|
||||||
|
void SetDeathLinkTag();
|
||||||
const nlohmann::json GetSlotData();
|
const nlohmann::json GetSlotData();
|
||||||
const std::vector<ApItem>& GetScoutedItems();
|
const std::vector<ApItem>& GetScoutedItems();
|
||||||
|
|
||||||
@@ -65,6 +68,7 @@ class ArchipelagoClient {
|
|||||||
std::unique_ptr<APClient> apClient;
|
std::unique_ptr<APClient> apClient;
|
||||||
bool itemQueued;
|
bool itemQueued;
|
||||||
bool disconnecting;
|
bool disconnecting;
|
||||||
|
bool isDeathLinkedDeath;
|
||||||
int retries;
|
int retries;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -69,6 +69,13 @@ void ArchipelagoSettingsWindow::DrawElement() {
|
|||||||
}
|
}
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
ImGui::SeparatorText("Additional Options");
|
||||||
|
if (UIWidgets::CVarCheckbox("Death Link", CVAR_REMOTE_ARCHIPELAGO("DeathLink"),
|
||||||
|
UIWidgets::CheckboxOptions()
|
||||||
|
.Color(THEME_COLOR).Tooltip("You die, others die. Others die, you die!"))) {
|
||||||
|
apClient.SetDeathLinkTag();
|
||||||
|
}
|
||||||
|
|
||||||
static bool sArchipelagoTexturesLoaded = false;
|
static bool sArchipelagoTexturesLoaded = false;
|
||||||
if (!sArchipelagoTexturesLoaded) {
|
if (!sArchipelagoTexturesLoaded) {
|
||||||
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage(
|
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage(
|
||||||
|
|||||||
@@ -3555,6 +3555,7 @@ void func_80836448(PlayState* play, Player* this, LinkAnimationHeader* anim) {
|
|||||||
Audio_PlayFanfare(NA_BGM_GAME_OVER);
|
Audio_PlayFanfare(NA_BGM_GAME_OVER);
|
||||||
gSaveContext.seqId = (u8)NA_BGM_DISABLED;
|
gSaveContext.seqId = (u8)NA_BGM_DISABLED;
|
||||||
gSaveContext.natureAmbienceId = NATURE_ID_DISABLED;
|
gSaveContext.natureAmbienceId = NATURE_ID_DISABLED;
|
||||||
|
GameInteractor_ExecuteOnPlayerDeath();
|
||||||
}
|
}
|
||||||
|
|
||||||
OnePointCutscene_Init(play, 9806, cond ? 120 : 60, &this->actor, MAIN_CAM);
|
OnePointCutscene_Init(play, 9806, cond ? 120 : 60, &this->actor, MAIN_CAM);
|
||||||
|
|||||||
Reference in New Issue
Block a user