Compare commits

...

25 Commits

Author SHA1 Message Date
Phillip Stephens c803bfb545 Update aurora 2026-04-29 09:14:51 -07:00
qwertyquerty 2623c44cab Merge pull request #583 from TheLastPocket/achievement/email-me
Email me and Heavy Hitter Achievements
2026-04-29 05:29:50 -07:00
madeline 24dd02fc81 better back in time condition 2026-04-29 05:28:28 -07:00
TakaRikka a97602b6dc Merge pull request #589 from TwilitRealm/instant-text-shop-fix
Fix instant text clearing some shop dialogue too quickly
2026-04-29 00:37:51 -07:00
TakaRikka e2943e90dc Merge pull request #590 from dooplecks/map-outline
Improve map outline rendering
2026-04-29 00:37:04 -07:00
doop afe54f22ab Write circle pixels linearly 2026-04-29 04:16:24 +00:00
doop e15f5bcee9 Improve map outline rendering 2026-04-29 03:51:14 +00:00
gymnast86 b26896cad5 includes 2026-04-28 18:05:40 -07:00
gymnast86 f75faf6b06 fix instant text clearing shop messages too early 2026-04-28 18:03:06 -07:00
TakaRikka 3e05789b58 Merge pull request #586 from TwilitRealm/fix/fix-super-clawshot
Fix draw crash with Super Clawshot enabled
2026-04-28 13:34:24 -07:00
MelonSpeedruns 4d12cc8ea2 Fix draw crash with Super Clawshot enabled 2026-04-28 11:05:29 -04:00
Julian Maynes 2ed2268579 fix newline 2026-04-28 07:19:18 -07:00
Julian Maynes 94a99e8da0 Add iron boots achievement 2026-04-28 07:12:29 -07:00
madeline ddaf50c01d long jump attack achievement 2026-04-28 06:53:20 -07:00
madeline 7566949b42 fix friendly fire achievement 2026-04-28 06:21:41 -07:00
Julian Maynes bb6db3caea remove newlines 2026-04-28 06:18:40 -07:00
Julian Maynes 2dc494dc1c Finish achievement 2026-04-28 06:14:56 -07:00
madeline 3c25633ee9 speed_target avoid ub 2026-04-28 06:08:04 -07:00
madeline d99ed2729b fix achievements window sizing 2026-04-28 05:57:58 -07:00
madeline 782455d48b Merge branch 'main' of https://github.com/TakaRikka/dusk 2026-04-28 05:54:12 -07:00
madeline 47863b34c2 achievement signal system 2026-04-28 05:54:09 -07:00
TakaRikka 92391d5eb8 add overview and ai notice to readme 2026-04-28 04:06:29 -07:00
madeline 0d37cb4e54 thank you berry much achievement 2026-04-28 04:01:22 -07:00
madeline ea528ed9d9 clear single achievement and fix achievement tab moving around 2026-04-28 03:41:04 -07:00
TakaRikka 30b7087f30 add option for only fishing once for sera's cat 2026-04-27 22:04:42 -07:00
18 changed files with 289 additions and 24 deletions
+6
View File
@@ -3,6 +3,10 @@
- ### **[Official Website](https://twilitrealm.dev)**
- ### **[Discord](https://discord.gg/QACynxeyna)**
# Overview
Dusk is a reverse-engineered reimplementation of Twilight Princess.
It aims to be as accurate as possible to the original while also providing new options, enhancements, and tools to customize your experience.
# Setup
**⚠️ Dusk does NOT provide any copyrighted assets. You must provide your own copy of the game.**
@@ -27,5 +31,7 @@ First make sure your dump of the game is clean and supported by Dusk. You can do
# Building
If you'd like to build Dusk from source, please read the [build instructions](docs/building.md).
Pull Requests are welcomed! Note that we do not accept contributions that are primarily AI generated and will close your PR if we suspect as much.
# Credits
Special thanks to the [TP decompilation](https://github.com/zeldaret/tp) team, the GC/Wii decompilation community, the [Aurora](https://github.com/encounter/aurora) developers, the [TP speedrunning community](https://zsrtp.link), and all [contributors](https://github.com/TwilitRealm/dusk/graphs/contributors).
+1 -1
+3
View File
@@ -67,6 +67,9 @@ public:
bool isStaffMessage();
bool isSaveMessage();
bool isTalkMessage();
#if TARGET_PC
bool isShopItemMessage();
#endif
const char* getSmellName();
const char* getPortalName();
const char* getBombName();
+9
View File
@@ -4,6 +4,8 @@
#include <functional>
#include <queue>
#include <string>
#include <string_view>
#include <unordered_set>
#include <vector>
#include "nlohmann/json.hpp"
@@ -14,6 +16,7 @@ enum class AchievementCategory : uint8_t {
Collection,
Challenge,
Minigame,
Misc,
Glitched
};
@@ -40,6 +43,11 @@ public:
void save();
void tick();
void clearAll();
void clearOne(const char* key);
// Signals are visible to all achievement checks within the same tick, then cleared.
void signal(const char* key);
bool hasSignal(const char* key) const;
std::vector<Achievement> getAchievements() const;
bool hasPendingUnlock() const { return !m_pendingUnlocks.empty(); }
@@ -57,6 +65,7 @@ private:
void processEntry(Entry& e);
std::vector<Entry> m_entries;
std::unordered_set<std::string_view> m_signals;
bool m_loaded = false;
bool m_dirty = false;
std::queue<std::string> m_pendingUnlocks;
+1
View File
@@ -76,6 +76,7 @@ struct UserSettings {
ConfigVar<bool> fastClimbing;
ConfigVar<bool> noMissClimbing;
ConfigVar<bool> fastTears;
ConfigVar<bool> no2ndFishForCat;
ConfigVar<bool> instantSaves;
ConfigVar<bool> instantText;
ConfigVar<bool> sunsSong;
+13
View File
@@ -8,6 +8,10 @@
#include "d/actor/d_a_horse.h"
#include "d/actor/d_a_crod.h"
#include "d/d_msg_object.h"
#ifdef TARGET_PC
#include "d/actor/d_a_obj_carry.h"
#include "dusk/achievements.h"
#endif
#if DEBUG
#include "d/d_s_menu.h"
@@ -677,6 +681,15 @@ BOOL daAlink_c::checkDamageAction() {
}
setDamagePoint(dmg, at_mtrl == dCcD_MTRL_FIRE || at_mtrl == dCcD_MTRL_ICE, TRUE, 0);
#ifdef TARGET_PC
if (tghit_ac_name == fpcNm_Obj_Carry_e) {
auto* carry = static_cast<daObjCarry_c*>(tghit_ac);
if (carry->prm_chk_type_ironball() && carry->checkCannon()) {
dusk::AchievementSystem::get().signal("iron_ball_hit_player");
}
}
#endif
if (armor_no_dmg && at_mtrl != dCcD_MTRL_ELECTRIC && at_mtrl != dCcD_MTRL_ICE) {
setGuardSe(var_r29);
+4
View File
@@ -18,6 +18,10 @@ enum {
};
void daAlink_c::hsChainShape_c::draw() {
if (dusk::getSettings().game.superClawshot) {
return;
}
static const int dummy = 0;
daAlink_c* alink = (daAlink_c*)getUserArea();
+5
View File
@@ -282,6 +282,11 @@ static void e_th_spin_B(e_th_class* i_this) {
i_this->current.pos += spC;
f32 speed_target;
#if AVOID_UB
speed_target = 0;
#endif
f32 anm_frame = i_this->mpModelMorf->getFrame();
switch (i_this->mMode) {
+2 -3
View File
@@ -956,7 +956,7 @@ static void npc_ne_tame(npc_ne_class* i_this) {
i_this->mpMorf->setPlaySpeed(i_this->mAnmSpeed);
/* dSv_event_flag_c::F_0470 - Fishing Pond - Reserved for fishing */
if (dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[470])) {
if (IF_DUSK(dusk::getSettings().game.no2ndFishForCat) || dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[470])) {
if (fpcEx_Search(s_fish_sub, _this) != NULL) {
i_this->mAction = npc_ne_class::ACT_HOME;
i_this->mMode = 10;
@@ -2948,8 +2948,7 @@ static int daNpc_Ne_Execute(npc_ne_class* i_this) {
if (i_this->mWantsFish && (i_this->mCounter & 0xf) == 0) {
/* dSv_event_flag_c::F_0470 - Fishing Pond - Reserved for fishing */
if (dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[470])
&& i_this->mDistToTarget < 1500.0f) {
if ((IF_DUSK(dusk::getSettings().game.no2ndFishForCat) || dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[470])) && i_this->mDistToTarget < 1500.0f) {
if (fopAcM_SearchByName(fpcNm_MG_ROD_e) != NULL) {
i_this->mNoFollow = false;
} else {
+43
View File
@@ -16,6 +16,7 @@
#ifdef TARGET_PC
constexpr u16 kMapResolutionMultiplier = 4;
constexpr u16 kMapCircleSize = 16 * kMapResolutionMultiplier;
#endif
void dMpath_n::dTexObjAggregate_c::create() {
@@ -32,6 +33,48 @@ void dMpath_n::dTexObjAggregate_c::create() {
JUT_ASSERT(74, image->magFilter == GX_NEAR);
mDoLib_setResTimgObj(image, mp_texObj[lp1], 0, NULL);
}
#if TARGET_PC
auto hqCircle = JKR_NEW TGXTexObj();
static bool hqCircleDrawn = false;
static u8 hqCircleData[kMapCircleSize * kMapCircleSize];
if (!hqCircleDrawn) {
const auto center = kMapCircleSize / 2.0f;
const auto radiusSq = center * center;
const auto blocksAcross = kMapCircleSize >> 3;
const auto totalPixels = sizeof(hqCircleData);
for (size_t i = 0; i < totalPixels; i++) {
// 8x4 block swizzling for I8
const auto blockIdx = i >> 5;
const auto localIdx = i & 31;
const auto blockY = blockIdx / blocksAcross;
const auto blockX = blockIdx % blocksAcross;
const auto localY = localIdx >> 3;
const auto localX = localIdx & 7;
const auto x = (blockX << 3) + localX;
const auto y = (blockY << 2) + localY;
const auto dx = (x + 0.5f) - center;
const auto dy = (y + 0.5f) - center;
// the original texture is in I4 format and uses 1 to indicate if inside the circle
// so we scale to I8 range: 255 / 15 = 17
hqCircleData[i] = (dx * dx + dy * dy < radiusSq) ? 17 : 0;
}
hqCircleDrawn = true;
}
GXInitTexObj(hqCircle, hqCircleData, kMapCircleSize, kMapCircleSize, GX_TF_I8, GX_CLAMP,
GX_CLAMP, GX_FALSE);
GXInitTexObjLOD(hqCircle, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1);
mp_texObj[6] = hqCircle;
#endif
}
void dMpath_n::dTexObjAggregate_c::remove() {
+8
View File
@@ -17,6 +17,10 @@
#include "d/d_msg_scrn_arrow.h"
#include "d/d_lib.h"
#ifdef TARGET_PC
#include "dusk/achievements.h"
#endif
#if VERSION == VERSION_GCN_JPN
#define D_MENU_LETTER_LINE_MAX 9
#else
@@ -514,6 +518,10 @@ void dMenu_Letter_c::read_open_init() {
setAButtonString(0);
setBButtonString(0);
mpBlackTex->setAlpha(0);
#ifdef TARGET_PC
dusk::AchievementSystem::get().signal("open_letter");
#endif
}
void dMenu_Letter_c::read_open_move() {
+1 -8
View File
@@ -1987,13 +1987,6 @@ bool jmessage_tSequenceProcessor::do_isReady() {
}
#endif
#if TARGET_PC
if (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) {
field_0xb2 = 1;
pReference->setSendTimer(0);
}
#endif
if (dComIfGp_checkMesgBgm()) {
bool isItemMusicPlaying = true;
if (mDoAud_checkPlayingSubBgmFlag() != Z2BGM_ITEM_GET &&
@@ -2066,7 +2059,7 @@ bool jmessage_tSequenceProcessor::do_isReady() {
case 0:
case 5:
case 6:
if (mDoCPd_c::getTrigA(PAD_1) || field_0xb2 != 0) {
if (mDoCPd_c::getTrigA(PAD_1) || field_0xb2 != 0 IF_DUSK(|| (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)))) {
field_0xa4 = 0;
pReference->onBatchFlag();
pReference->setCharCnt(D_MSG_CLASS_CHAR_CNT_MAX);
+38 -1
View File
@@ -32,6 +32,9 @@
#if TARGET_PC
#include "dusk/settings.h"
#include <vector>
#include <array>
#include <algorithm>
#endif
static void dMsgObject_addFundRaising(s16 param_0);
@@ -1594,7 +1597,7 @@ u8 dMsgObject_c::isSend() {
return 2;
}
} else {
if (IF_DUSK((dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) ||)
if (IF_DUSK((dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0) && !isShopItemMessage()) ||)
mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) {
return 2;
}
@@ -1866,6 +1869,40 @@ bool dMsgObject_c::isTalkMessage() {
return true;
}
#if TARGET_PC
bool dMsgObject_c::isShopItemMessage() {
// Probably a better way to do this than just listing every message id, but this works for now
// Note: Keep contents sorted so we can use binary search
const auto shopMsgIds = std::to_array<std::vector<s16>>({
{},
// zel_01.bmg - Seras Shop
{7001, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7013, 7014, 7022, 7023, 7028, 7029,
7044, 7045, 7053},
// zel_02.bmg - Kakariko Shops
{5251, 5253, 5254, 5256, 5258, 5259, 5653, 5654, 5656, 5660, 5661, 5664, 5665, 5697, 5698,
5699, 5803, 5804, 5806, 5810, 5811, 5812, 5814, 5821, 5823, 5824, 5987, 5988, 5989, 5990,
5991, 5992, 5993, 5994, 5995, 5996, 5997, 5998, 5999},
// zel_03.bmg - Death Mountain Shop
{5303, 5304, 5306, 5310, 5311, 5314, 5315, 5322, 5323, 5324, 5496, 5497, 5498, 5499},
// zel_04.bmg - Castle Town Shops
{5407, 5408, 5409, 5410, 5411, 5412, 5413, 5414, 5415, 5416, 5417, 5418, 5419, 5420, 5431,
5432, 5433, 5434, 5435, 5436, 5437, 5438, 5439, 5440, 5441, 5444, 5449, 5450, 5451, 5452,
5462},
// zel_05.bmg - Oocca Shop
{9428, 9429, 9430, 9431, 9432, 9437, 9443, 9448, 9449, 9451, 9459}
});
u16 id = mMessageID;
s16 group = dMsgObject_getGroupID();
if (group < shopMsgIds.size()) {
return std::ranges::binary_search(shopMsgIds[group], id);
}
return false;
}
#endif
const char* dMsgObject_c::getSmellName() {
JMSMesgInfo_c* info_header_p = (JMSMesgInfo_c*)((char*)mpMsgRes + 0x20);
char* data_ptr = (char*)info_header_p + info_header_p->header.size;
+127 -8
View File
@@ -8,6 +8,7 @@
#include "d/actor/d_a_player.h"
#include "d/d_demo.h"
#include "f_pc/f_pc_name.h"
#include "f_op/f_op_actor_mng.h"
#include <filesystem>
#include <algorithm>
@@ -46,6 +47,21 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
},
{}
},
{
{
"plumm_max",
"Thank You Berry Much",
"Score 61,454 points in the Plumm minigame.",
AchievementCategory::Minigame,
false, 0, 0, false
},
[](Achievement& a, json&) {
if (dComIfGs_getBalloonScore() >= 61454) {
a.progress = 1;
}
},
{}
},
{
{
"rollgoal_8",
@@ -258,6 +274,58 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
},
{}
},
{
{
"friendly_fire",
"Friendly Fire",
"Get hit by your own cannonball.",
AchievementCategory::Misc,
false, 0, 0, false
},
[](Achievement& a, json&) {
if (AchievementSystem::get().hasSignal("iron_ball_hit_player")) {
a.progress = 1;
}
},
{}
},
{
{
"long_jump_attack",
"Long Jump Attack",
"Travel more than 20 meters in a single jump attack before landing.",
AchievementCategory::Misc,
false, 0, 0, false
},
[](Achievement& a, json&) {
static bool inJump = false;
static float startX = 0.0f, startZ = 0.0f;
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
if (link == nullptr) {
inJump = false;
return;
}
if (!inJump) {
if (link->mProcID == daAlink_c::PROC_CUT_JUMP) {
inJump = true;
startX = link->current.pos.x;
startZ = link->current.pos.z;
}
} else if (link->mProcID == daAlink_c::PROC_CUT_JUMP_LAND) {
inJump = false;
const float dx = link->current.pos.x - startX;
const float dz = link->current.pos.z - startZ;
if (dx * dx + dz * dz >= 2000.0f * 2000.0f) {
a.progress = 1;
}
} else if (link->mProcID != daAlink_c::PROC_CUT_JUMP) {
inJump = false;
}
},
{}
},
{
{
"back_in_time",
@@ -267,18 +335,13 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
false, 0, 0, false
},
[](Achievement& a, json&) {
static int titleNoDemoFrames = 0;
if (fopAcM_SearchByName(fpcNm_TITLE_e) == nullptr) {
titleNoDemoFrames = 0;
return;
}
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
if (link != nullptr && dDemo_c::getMode() == 0) {
if (++titleNoDemoFrames >= 60) {
const auto* player = static_cast<const daPy_py_c*>(daPy_getPlayerActorClass());
if (player != nullptr && player->mDemo.getDemoMode() == 1) {
a.progress = 1;
}
} else {
titleNoDemoFrames = 0;
}
},
{}
@@ -345,6 +408,41 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
}
},
{}
},
{
{
"email_me",
"Email Me",
"Read a letter during the Dark Beast Ganon fight.",
AchievementCategory::Misc,
false, 0, 0, false
},
[](Achievement& a, json&) {
void* dbgExists = fopAcM_SearchByName(fpcNm_B_MGN_e);
if (dbgExists && AchievementSystem::get().hasSignal("open_letter")) {
a.progress = 1;
}
},
{}
},
{
{
"heavy-hitter",
"Heavy Hitter",
"Wear the Iron Boots during the end credits.",
AchievementCategory::Misc,
false, 0, 0, false
},
[](Achievement& a, json&) {
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
if (link == nullptr || link->mProcID != daAlink_c::PROC_GANON_FINISH) {
return;
}
if (daPy_getPlayerActorClass()->checkEquipHeavyBoots()) {
a.progress = 1;
}
},
{}
}
};
}
@@ -426,6 +524,26 @@ void AchievementSystem::clearAll() {
save();
}
void AchievementSystem::signal(const char* key) {
m_signals.insert(key);
}
bool AchievementSystem::hasSignal(const char* key) const {
return m_signals.count(key) > 0;
}
void AchievementSystem::clearOne(const char* key) {
for (auto& e : m_entries) {
if (std::string(e.achievement.key) == key) {
e.achievement.progress = 0;
e.achievement.unlocked = false;
e.extra = {};
break;
}
}
save();
}
void AchievementSystem::processEntry(Entry& e) {
if (e.achievement.unlocked) {
return;
@@ -458,6 +576,7 @@ void AchievementSystem::tick() {
for (auto& e : m_entries) {
processEntry(e);
}
m_signals.clear();
if (m_dirty) {
save();
m_dirty = false;
+20 -3
View File
@@ -76,8 +76,8 @@ void ImGuiAchievements::draw(bool& open) {
return;
}
ImGui::SetNextWindowSizeConstraints(ImVec2(640, 200), ImVec2(800, 900));
ImGui::SetNextWindowSize(ImVec2(640, 480), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints(ImVec2(800, 200), ImVec2(1280, 900));
ImGui::SetNextWindowSize(ImVec2(800, 480), ImGuiCond_FirstUseEver);
if (!ImGui::Begin(
"Achievements", &open,
@@ -111,6 +111,7 @@ void ImGuiAchievements::draw(bool& open) {
{AchievementCategory::Collection, "Collection", ImVec4(0.3f, 0.85f, 0.4f, 1.0f)},
{AchievementCategory::Challenge, "Challenge", ImVec4(1.0f, 0.65f, 0.15f, 1.0f)},
{AchievementCategory::Minigame, "Minigame", ImVec4(0.5f, 0.85f, 1.0f, 1.0f)},
{AchievementCategory::Misc, "Misc", ImVec4(0.65f, 0.65f, 0.65f, 1.0f)},
{AchievementCategory::Glitched, "Glitched", ImVec4(0.75f, 0.4f, 1.0f, 1.0f)},
};
@@ -131,7 +132,7 @@ void ImGuiAchievements::draw(bool& open) {
continue;
}
const std::string tabLabel = fmt::format("{} ({}/{})", catInfo.label, catUnlocked, catTotal);
const std::string tabLabel = fmt::format("{} ({}/{})###{}", catInfo.label, catUnlocked, catTotal, catInfo.label);
ImGui::PushStyleColor(ImGuiCol_Text, catInfo.color);
const bool tabOpen = ImGui::BeginTabItem(tabLabel.c_str());
@@ -152,6 +153,7 @@ void ImGuiAchievements::draw(bool& open) {
continue;
}
ImGui::PushID(a.key);
ImGui::BeginGroup();
ImGui::PushStyleColor(
ImGuiCol_Text,
@@ -190,6 +192,21 @@ void ImGuiAchievements::draw(bool& open) {
ImGui::PopStyleColor();
}
ImGui::EndGroup();
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
ImGui::OpenPopup("##ctx");
}
if (ImGui::BeginPopup("##ctx")) {
ImGui::TextDisabled("%s", a.name);
ImGui::Separator();
if (ImGui::MenuItem("Clear Achievement")) {
AchievementSystem::get().clearOne(a.key);
}
ImGui::EndPopup();
}
ImGui::Spacing();
ImGui::PopID();
}
+1
View File
@@ -30,6 +30,7 @@ static void ApplyPresetHD() {
s.game.biggerWallets.setValue(true);
s.game.invertCameraXAxis.setValue(true);
s.game.freeCamera.setValue(true);
s.game.no2ndFishForCat.setValue(true);
}
static void ApplyPresetDusk() {
+5
View File
@@ -267,6 +267,11 @@ namespace dusk {
ImGui::SetTooltip("Link won't recoil when his sword hits walls.");
}
config::ImGuiCheckbox("No 2nd Fish for Cat", getSettings().game.no2ndFishForCat);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Only need to fish once for Sera's cat to return.");
}
config::ImGuiCheckbox("Skip TV Settings Screen", getSettings().game.hideTvSettingsScreen);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skip the TV calibration screen shown when loading a save.");
+2
View File
@@ -36,6 +36,7 @@ UserSettings g_userSettings = {
.fastClimbing {"game.fastClimbing", false},
.noMissClimbing {"game.noMissClimbing", false},
.fastTears {"game.fastTears", false},
.no2ndFishForCat {"game.no2ndFishForCat", false},
.instantSaves {"game.instantSaves", false},
.instantText {"game.instantText", false},
.sunsSong {"game.sunsSong", false},
@@ -147,6 +148,7 @@ void registerSettings() {
Register(g_userSettings.game.instantDeath);
Register(g_userSettings.game.fastClimbing);
Register(g_userSettings.game.fastTears);
Register(g_userSettings.game.no2ndFishForCat);
Register(g_userSettings.game.instantSaves);
Register(g_userSettings.game.instantText);
Register(g_userSettings.game.sunsSong);