mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-05-26 23:26:45 -04:00
40 achievements
This commit is contained in:
@@ -1845,6 +1845,12 @@ inline void dComIfGs_addDeathCount() {
|
||||
g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().addDeathCount();
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
inline u16 dComIfGs_getDeathCount() {
|
||||
return g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().getDeathCount();
|
||||
}
|
||||
#endif
|
||||
|
||||
inline char* dComIfGs_getPlayerName() {
|
||||
return g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().getPlayerName();
|
||||
}
|
||||
|
||||
@@ -486,6 +486,9 @@ public:
|
||||
mDeathCount++;
|
||||
}
|
||||
}
|
||||
#if TARGET_PC
|
||||
u16 getDeathCount() const { return mDeathCount; }
|
||||
#endif
|
||||
char* getPlayerName() const { return const_cast<char*>(mPlayerName); }
|
||||
void setPlayerName(const char* i_name) {
|
||||
#if AVOID_UB
|
||||
|
||||
@@ -205,6 +205,9 @@ int daAlink_c::setDamagePoint(int i_dmgAmount, BOOL i_checkZoraMag, BOOL i_setDm
|
||||
dComIfGp_setItemLifeCount(-i_dmgAmount, 0);
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
dusk::AchievementSystem::get().signal("player_damaged");
|
||||
#endif
|
||||
onResetFlg1(RFLG1_DAMAGE_IMPACT);
|
||||
mSwordUpTimer = 0;
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
#include "d/actor/d_a_e_pz.h"
|
||||
#include "d/actor/d_a_horse.h"
|
||||
#include "d/actor/d_a_hozelda.h"
|
||||
#if TARGET_PC
|
||||
#include "dusk/achievements.h"
|
||||
#endif
|
||||
|
||||
int daArrow_c::createHeap() {
|
||||
J3DModelData* model_data;
|
||||
@@ -92,7 +95,12 @@ void daArrow_c::atHitCallBack(dCcD_GObjInf* i_atObjInf, fopAc_ac_c* i_tgActor, d
|
||||
if (dist_to_hitpos < field_0x998) {
|
||||
field_0x998 = dist_to_hitpos;
|
||||
mHitAcID = fopAcM_GetID(i_tgActor);
|
||||
|
||||
#if TARGET_PC
|
||||
if (fopAcM_GetGroup(i_tgActor) == fopAc_ENEMY_e &&
|
||||
current.pos.abs(mStartPos) > 10000.0f) {
|
||||
dusk::AchievementSystem::get().signal("arrow_hit_100m");
|
||||
}
|
||||
#endif
|
||||
if (mArrowType == 1) {
|
||||
field_0x9a8 = *hit_pos_p;
|
||||
} else if (i_tgObjInf->ChkTgShield()) {
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#include "dusk/settings.h"
|
||||
#if TARGET_PC
|
||||
#include "dusk/achievements.h"
|
||||
#endif
|
||||
|
||||
class daB_GND_HIO_c : public JORReflexible {
|
||||
public:
|
||||
@@ -1289,6 +1292,9 @@ static void b_gnd_g_wait(b_gnd_class* i_this) {
|
||||
if (i_this->mMoveMode < 5 && i_this->mPlayerDistXZ < 600.0f) {
|
||||
i_this->mMoveMode = 5;
|
||||
i_this->field_0xc44[0] = 10;
|
||||
#if TARGET_PC
|
||||
dusk::AchievementSystem::get().signal("ganondorf_fishing_rod");
|
||||
#endif
|
||||
}
|
||||
} else if (i_this->mMoveMode == 5) {
|
||||
i_this->mMoveMode = 6;
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#include "c/c_damagereaction.h"
|
||||
#include "f_op/f_op_actor_enemy.h"
|
||||
#include "f_op/f_op_camera_mng.h"
|
||||
#if TARGET_PC
|
||||
#include "dusk/achievements.h"
|
||||
#endif
|
||||
|
||||
class daE_TH_HIO_c : public JORReflexible {
|
||||
public:
|
||||
@@ -542,6 +545,7 @@ static void damage_check(e_th_class* i_this) {
|
||||
if (i_this->field_0x6a4 == 0 && i_this->mAction != ACTION_SPIN) {
|
||||
daPy_py_c* player = (daPy_py_c*)dComIfGp_getPlayer(0);
|
||||
OS_REPORT("E_th HP1 %d\n", i_this->health);
|
||||
s16 prevHealth = i_this->health;
|
||||
cc_at_check(i_this, &i_this->mAtInfo);
|
||||
OS_REPORT("E_th HP2 %d\n", i_this->health);
|
||||
|
||||
@@ -554,6 +558,11 @@ static void damage_check(e_th_class* i_this) {
|
||||
dComIfGs_onOneZoneSwitch(3, -1);
|
||||
|
||||
if (i_this->health <= 0) {
|
||||
#if TARGET_PC
|
||||
if (prevHealth == i_this->field_0x560) {
|
||||
dusk::AchievementSystem::get().signal("dark_hammer_one_hit");
|
||||
}
|
||||
#endif
|
||||
i_this->mAction = ACTION_END;
|
||||
i_this->mMode = 0;
|
||||
i_this->field_0x68a |= 4;
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#include "d/d_s_play.h"
|
||||
#include "d/d_com_inf_game.h"
|
||||
#include "f_op/f_op_actor_mng.h"
|
||||
#if TARGET_PC
|
||||
#include "dusk/achievements.h"
|
||||
#endif
|
||||
|
||||
static int plCutLRC[58] = {
|
||||
0, //
|
||||
@@ -434,6 +437,11 @@ fopAc_ac_c* cc_at_check(fopAc_ac_c* i_enemy, dCcU_AtInfo* i_AtInfo) {
|
||||
if (i_AtInfo->mAttackPower != 0 && i_enemy->health <= 0) {
|
||||
i_AtInfo->mHitStatus = 2;
|
||||
i_enemy->health = 0;
|
||||
#if TARGET_PC
|
||||
if (fopAcM_GetGroup(i_enemy) == fopAc_ENEMY_e) {
|
||||
dusk::AchievementSystem::get().signal("enemy_killed");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int uvar8;
|
||||
|
||||
+599
-134
@@ -2,11 +2,16 @@
|
||||
#include "dusk/io.hpp"
|
||||
#include "dusk/main.h"
|
||||
#include "d/d_com_inf_game.h"
|
||||
#include "d/d_item_data.h"
|
||||
#include "d/d_map_path_fmap.h"
|
||||
#include "d/d_stage.h"
|
||||
#include "d/d_menu_fmap.h"
|
||||
#include "JSystem/JKernel/JKRArchive.h"
|
||||
#include "d/d_meter2_info.h"
|
||||
#include "d/actor/d_a_alink.h"
|
||||
#include "d/actor/d_a_ni.h"
|
||||
#include "d/actor/d_a_npc4.h"
|
||||
#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"
|
||||
|
||||
@@ -17,6 +22,14 @@ namespace dusk {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
static void* s_cucco_play_search(void* i_actor, void*) {
|
||||
if (!fopAcM_IsActor(i_actor) || fopAcM_GetName((fopAc_ac_c*)i_actor) != fpcNm_NI_e) {
|
||||
return nullptr;
|
||||
}
|
||||
auto* ni = static_cast<ni_class*>(i_actor);
|
||||
return ni->mAction == ACTION_PLAY_e ? i_actor : nullptr;
|
||||
}
|
||||
|
||||
static void checkGoatHerding(Achievement& a, int32_t threshMs) {
|
||||
if (dMeter2Info_getMaxCount() != 20 || dMeter2Info_getNowCount() != 20) {
|
||||
return;
|
||||
@@ -31,6 +44,7 @@ static constexpr auto ACHIEVEMENTS_FILENAME = "achievements.json";
|
||||
|
||||
std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
return {
|
||||
// Story
|
||||
{
|
||||
{
|
||||
"hero_of_twilight",
|
||||
@@ -47,6 +61,433 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"completionist",
|
||||
"Completionist",
|
||||
"100% the game.",
|
||||
AchievementCategory::Story,
|
||||
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 (dComIfGs_getMaxLife() < 100) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (!dComIfGs_isCollectMirror(i)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (!dComIfGs_isCollectCrystal(i)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
static const u16 skillBits[] = {
|
||||
dSv_event_flag_c::F_0338, dSv_event_flag_c::F_0339,
|
||||
dSv_event_flag_c::F_0340, dSv_event_flag_c::F_0341,
|
||||
dSv_event_flag_c::F_0342, dSv_event_flag_c::F_0343,
|
||||
dSv_event_flag_c::F_0344
|
||||
};
|
||||
for (u16 bit : skillBits) {
|
||||
if (!dComIfGs_isEventBit(bit)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (dComIfGs_checkGetInsectNum() < 24) {
|
||||
return;
|
||||
}
|
||||
if (dComIfGs_getPohSpiritNum() < 60) {
|
||||
return;
|
||||
}
|
||||
if (dComIfGs_getWalletSize() < 2) {
|
||||
return;
|
||||
}
|
||||
if (dComIfGs_getArrowMax() < 100) {
|
||||
return;
|
||||
}
|
||||
if (!dComIfGs_isCollectSword(COLLECT_MASTER_SWORD)) {
|
||||
return;
|
||||
}
|
||||
if (!dComIfGs_isCollectShield(COLLECT_HYLIAN_SHIELD)) {
|
||||
return;
|
||||
}
|
||||
if (!dComIfGs_isCollectClothes(KOKIRI_CLOTHES_FLAG)) {
|
||||
return;
|
||||
}
|
||||
if (!dComIfGs_isItemFirstBit(dItemNo_WEAR_ZORA_e)) {
|
||||
return;
|
||||
}
|
||||
if (!dComIfGs_isItemFirstBit(dItemNo_ARMOR_e)) {
|
||||
return;
|
||||
}
|
||||
static const struct { int stage; int sw; } warpPortals[] = {
|
||||
{ dStage_SaveTbl_ORDON, 52 }, // Ordon Spring
|
||||
{ dStage_SaveTbl_FARON, 71 }, // South Faron Woods
|
||||
{ dStage_SaveTbl_FARON, 2 }, // North Faron Woods
|
||||
{ dStage_SaveTbl_GROVE, 100 }, // Sacred Grove
|
||||
{ dStage_SaveTbl_FIELD, 21 }, // Gorge
|
||||
{ dStage_SaveTbl_ELDIN, 31 }, // Kakariko Village
|
||||
{ dStage_SaveTbl_ELDIN, 21 }, // Death Mountain
|
||||
{ dStage_SaveTbl_FIELD, 99 }, // Bridge of Eldin
|
||||
{ dStage_SaveTbl_FIELD, 3 }, // Castle Town
|
||||
{ dStage_SaveTbl_LANAYRU, 10 }, // Lake Hylia
|
||||
{ dStage_SaveTbl_LANAYRU, 2 }, // Zora's Domain
|
||||
{ dStage_SaveTbl_LANAYRU, 21 }, // Upper Zora's River
|
||||
{ dStage_SaveTbl_SNOWPEAK, 21 }, // Snowpeak
|
||||
{ dStage_SaveTbl_DESERT, 21 }, // Gerudo Mesa
|
||||
{ dStage_SaveTbl_DESERT, 40 }, // Mirror Chamber
|
||||
};
|
||||
for (const auto& p : warpPortals) {
|
||||
if (!g_dComIfG_gameInfo.info.getSavedata().getSave(p.stage).getBit().isSwitch(p.sw)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (dComIfGs_getCollectSmell() == dItemNo_NONE_e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dMeter2Info_getRecieveLetterNum() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasJournal = false;
|
||||
for (int fi = 0; fi < 6; ++fi) {
|
||||
if (dComIfGs_getFishNum(fi) != 0) {
|
||||
hasJournal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasJournal) {
|
||||
return;
|
||||
}
|
||||
|
||||
int bottleCount = 0;
|
||||
for (int i = 0; i < dSv_player_item_c::BOTTLE_MAX; ++i) {
|
||||
if (dComIfGs_getItem(SLOT_11 + i, false) != dItemNo_NONE_e) {
|
||||
bottleCount++;
|
||||
}
|
||||
}
|
||||
if (bottleCount < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
int bombBagCount = 0;
|
||||
for (int i = 0; i < dSv_player_item_c::BOMB_BAG_MAX; ++i) {
|
||||
if (dComIfGs_getItem(SLOT_15 + i, false) != dItemNo_NONE_e) {
|
||||
bombBagCount++;
|
||||
}
|
||||
}
|
||||
if (bombBagCount < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasJewelRod = false;
|
||||
for (int slot = 0; slot < 24 && !hasJewelRod; ++slot) {
|
||||
const u8 item = dComIfGs_getItem(slot, false);
|
||||
if (item == dItemNo_JEWEL_ROD_e || item == dItemNo_JEWEL_BEE_ROD_e || item == dItemNo_JEWEL_WORM_ROD_e) {
|
||||
hasJewelRod = true;
|
||||
}
|
||||
}
|
||||
if (!hasJewelRod) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const u8 requiredWheelItems[] = {
|
||||
dItemNo_BOOMERANG_e,
|
||||
dItemNo_BOW_e,
|
||||
dItemNo_W_HOOKSHOT_e,
|
||||
dItemNo_SPINNER_e,
|
||||
dItemNo_IRONBALL_e,
|
||||
dItemNo_COPY_ROD_e,
|
||||
dItemNo_HVY_BOOTS_e,
|
||||
dItemNo_KANTERA_e,
|
||||
dItemNo_PACHINKO_e,
|
||||
dItemNo_HAWK_EYE_e,
|
||||
dItemNo_ANCIENT_DOCUMENT_e,
|
||||
dItemNo_HORSE_FLUTE_e,
|
||||
};
|
||||
for (u8 required : requiredWheelItems) {
|
||||
bool found = false;
|
||||
for (int slot = 0; slot < 24; ++slot) {
|
||||
if (dComIfGs_getItem(slot, false) == required) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
a.progress = 1;
|
||||
},
|
||||
{}
|
||||
},
|
||||
// Collection
|
||||
{
|
||||
{
|
||||
"princess_of_bugs",
|
||||
"The Princess of Bugs",
|
||||
"Deliver all 24 golden bugs to Agitha.",
|
||||
AchievementCategory::Collection,
|
||||
true, 24, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
a.progress = dComIfGs_checkGetInsectNum();
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"all_poes",
|
||||
"Poe Collector",
|
||||
"Collect all 60 Poe Souls.",
|
||||
AchievementCategory::Collection,
|
||||
true, 60, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
a.progress = dComIfGs_getPohSpiritNum();
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"hylian_loach",
|
||||
"Legendary Catch",
|
||||
"Catch a Hylian Loach.",
|
||||
AchievementCategory::Collection,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (dComIfGs_getFishNum(1) > 0) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"all_fish",
|
||||
"Gone Fishin'",
|
||||
"Catch all 6 species of fish.",
|
||||
AchievementCategory::Collection,
|
||||
true, 6, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
int nUniqueFish = 0;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
if (dComIfGs_getFishNum(i) != 0) {
|
||||
nUniqueFish++;
|
||||
}
|
||||
}
|
||||
a.progress = nUniqueFish;
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"a_big_heart",
|
||||
"A Big Heart",
|
||||
"Reach maximum health with all 20 heart containers.",
|
||||
AchievementCategory::Collection,
|
||||
true, 20, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
a.progress = dComIfGs_getMaxLife() / 5;
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"all_bottles",
|
||||
"Glassware Guardian",
|
||||
"Obtain all 4 bottles.",
|
||||
AchievementCategory::Collection,
|
||||
true, 4, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (daPy_getPlayerActorClass() == nullptr) {
|
||||
return;
|
||||
}
|
||||
int count = 0;
|
||||
for (int i = 0; i < dSv_player_item_c::BOTTLE_MAX; ++i) {
|
||||
if (dComIfGs_getItem(SLOT_11 + i, false) != dItemNo_NONE_e) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
a.progress = count;
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"all_hidden_skills",
|
||||
"Master of Secrets",
|
||||
"Learn all 7 Hidden Skills.",
|
||||
AchievementCategory::Collection,
|
||||
true, 7, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
static const u16 skillBits[] = {
|
||||
dSv_event_flag_c::F_0338, dSv_event_flag_c::F_0339,
|
||||
dSv_event_flag_c::F_0340, dSv_event_flag_c::F_0341,
|
||||
dSv_event_flag_c::F_0342, dSv_event_flag_c::F_0343,
|
||||
dSv_event_flag_c::F_0344
|
||||
};
|
||||
int count = 0;
|
||||
for (u16 bit : skillBits) {
|
||||
if (dComIfGs_isEventBit(bit)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
a.progress = count;
|
||||
},
|
||||
{}
|
||||
},
|
||||
// Challenge
|
||||
{
|
||||
{
|
||||
"cave_of_ordeals",
|
||||
"Conqueror of Ordeals",
|
||||
"Clear all 50 floors of the Cave of Ordeals.",
|
||||
AchievementCategory::Challenge,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (daNpcF_chkEvtBit(0x1F9)) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"cave_of_ordeals_heartless",
|
||||
"Indomitable",
|
||||
"Clear all 50 floors of the Cave of Ordeals with only 3 heart containers.",
|
||||
AchievementCategory::Challenge,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (daNpcF_chkEvtBit(0x1F9) && dComIfGs_getMaxLife() <= 15) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"speedrun_12h",
|
||||
"Been There Done That",
|
||||
"Defeat Ganondorf with a total save file play time under 12 hours.",
|
||||
AchievementCategory::Challenge,
|
||||
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;
|
||||
}
|
||||
const int64_t ticks = (static_cast<int64_t>(OSGetTime()) - dComIfGs_getSaveStartTime()) + dComIfGs_getSaveTotalTime();
|
||||
if (ticks / OS_TIMER_CLOCK < 12 * 3600) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"speedrun_8h",
|
||||
"Swift Blade",
|
||||
"Defeat Ganondorf with a total save file play time under 6 hours.",
|
||||
AchievementCategory::Challenge,
|
||||
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;
|
||||
}
|
||||
const int64_t ticks = (static_cast<int64_t>(OSGetTime()) - dComIfGs_getSaveStartTime()) + dComIfGs_getSaveTotalTime();
|
||||
if (ticks / OS_TIMER_CLOCK < 8 * 3600) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"dark_hammer_one_hit",
|
||||
"Mortal Edge",
|
||||
"Defeat Dark Hammer in a single hit.",
|
||||
AchievementCategory::Challenge,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (AchievementSystem::get().hasSignal("dark_hammer_one_hit")) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"no_deaths_clear",
|
||||
"Deathless",
|
||||
"Defeat Ganondorf with 0 deaths on your save file.",
|
||||
AchievementCategory::Challenge,
|
||||
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 (dComIfGs_getDeathCount() == 0) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"untouchable",
|
||||
"Untouchable",
|
||||
"Kill 25 enemies in a row without taking damage.",
|
||||
AchievementCategory::Challenge,
|
||||
true, 25, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
auto& sys = AchievementSystem::get();
|
||||
if (sys.hasSignal("player_damaged")) {
|
||||
a.progress = 0;
|
||||
}
|
||||
if (sys.hasSignal("enemy_killed")) {
|
||||
a.progress++;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"bow_100m_hit",
|
||||
"Long Shot",
|
||||
"Hit an enemy from over 100 meters away with the bow.",
|
||||
AchievementCategory::Challenge,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (AchievementSystem::get().hasSignal("arrow_hit_100m")) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
// Minigame
|
||||
{
|
||||
{
|
||||
"plumm_max",
|
||||
@@ -133,14 +574,16 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
},
|
||||
{
|
||||
{
|
||||
"cave_of_ordeals",
|
||||
"Conqueror of Ordeals",
|
||||
"Clear all 50 floors of the Cave of Ordeals.",
|
||||
AchievementCategory::Challenge,
|
||||
"snowboard_70s",
|
||||
"Downhill Dash",
|
||||
"Finish the snowboarding minigame in under 70 seconds.",
|
||||
AchievementCategory::Minigame,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (daNpcF_chkEvtBit(0x1F9)) {
|
||||
const int32_t bestMs = dComIfGs_getRaceGameTime();
|
||||
if (dComIfGs_isEventBit(dSv_event_flag_c::F_0481) &&
|
||||
bestMs > 0 && bestMs <= 70000) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
@@ -148,132 +591,28 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
},
|
||||
{
|
||||
{
|
||||
"cave_of_ordeals_heartless",
|
||||
"Indomitable",
|
||||
"Clear all 50 floors of the Cave of Ordeals with only 3 heart containers.",
|
||||
AchievementCategory::Challenge,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (daNpcF_chkEvtBit(0x1F9) && dComIfGs_getMaxLife() <= 15) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"speedrun_12h",
|
||||
"Been There Done That",
|
||||
"Defeat Ganondorf with a total save file play time under 12 hours.",
|
||||
AchievementCategory::Challenge,
|
||||
"canoe_perfect",
|
||||
"River Raider",
|
||||
"Achieve a perfect score in the canoe minigame.",
|
||||
AchievementCategory::Minigame,
|
||||
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) {
|
||||
if (link == nullptr) {
|
||||
return;
|
||||
}
|
||||
const int64_t ticks = (static_cast<int64_t>(OSGetTime()) - dComIfGs_getSaveStartTime()) + dComIfGs_getSaveTotalTime();
|
||||
if (ticks / OS_TIMER_CLOCK < 12 * 3600) {
|
||||
static bool wasInCanoe = false;
|
||||
bool inCanoe = link->mProcID >= daAlink_c::PROC_CANOE_RIDE &&
|
||||
link->mProcID <= daAlink_c::PROC_CANOE_KANDELAAR_POUR;
|
||||
if (wasInCanoe && !inCanoe && dMeter2Info_getNowCount() >= 30) {
|
||||
a.progress = 1;
|
||||
}
|
||||
wasInCanoe = inCanoe;
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"speedrun_8h",
|
||||
"Swift Blade",
|
||||
"Defeat Ganondorf with a total save file play time under 6 hours.",
|
||||
AchievementCategory::Challenge,
|
||||
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;
|
||||
}
|
||||
const int64_t ticks = (static_cast<int64_t>(OSGetTime()) - dComIfGs_getSaveStartTime()) + dComIfGs_getSaveTotalTime();
|
||||
if (ticks / OS_TIMER_CLOCK < 8 * 3600) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"princess_of_bugs",
|
||||
"The Princess of Bugs",
|
||||
"Deliver all 24 golden bugs to Agitha.",
|
||||
AchievementCategory::Collection,
|
||||
true, 24, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
a.progress = dComIfGs_checkGetInsectNum();
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"all_poes",
|
||||
"Poe Collector",
|
||||
"Collect all 60 Poe Souls.",
|
||||
AchievementCategory::Collection,
|
||||
true, 60, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
a.progress = dComIfGs_getPohSpiritNum();
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"hylian_loach",
|
||||
"Legendary Catch",
|
||||
"Catch a Hylian Loach.",
|
||||
AchievementCategory::Collection,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (dComIfGs_getFishNum(1) > 0) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"all_fish",
|
||||
"Gone Fishin'",
|
||||
"Catch all 6 species of fish.",
|
||||
AchievementCategory::Collection,
|
||||
true, 6, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
int nUniqueFish = 0;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
if (dComIfGs_getFishNum(i) != 0) {
|
||||
nUniqueFish++;
|
||||
}
|
||||
}
|
||||
a.progress = nUniqueFish;
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"a_big_heart",
|
||||
"A Big Heart",
|
||||
"Reach maximum health with all 20 heart containers.",
|
||||
AchievementCategory::Collection,
|
||||
true, 20, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
a.progress = dComIfGs_getMaxLife() / 5;
|
||||
},
|
||||
{}
|
||||
},
|
||||
// Misc
|
||||
{
|
||||
{
|
||||
"friendly_fire",
|
||||
@@ -326,6 +665,87 @@ 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;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"fishing_rod_ganondorf",
|
||||
"Here Fishy Fishy",
|
||||
"Confuse Ganondorf with the fishing rod.",
|
||||
AchievementCategory::Misc,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (AchievementSystem::get().hasSignal("ganondorf_fishing_rod")) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"steal_from_trill",
|
||||
"Petty Theft",
|
||||
"Steal from Trill.",
|
||||
AchievementCategory::Misc,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (dComIfGs_isEventBit(dSv_event_flag_c::F_0758)) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"cucco_control",
|
||||
"Cucco Whisperer",
|
||||
"Take control of a cucco.",
|
||||
AchievementCategory::Misc,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (fopAcM_Search(s_cucco_play_search, nullptr) != nullptr) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
// Glitched
|
||||
{
|
||||
{
|
||||
"back_in_time",
|
||||
@@ -411,34 +831,79 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
},
|
||||
{
|
||||
{
|
||||
"email_me",
|
||||
"Email Me",
|
||||
"Read a letter during the Dark Beast Ganon fight.",
|
||||
AchievementCategory::Misc,
|
||||
"no_fish_suit",
|
||||
"No Fish Suit No Problem",
|
||||
"Defeat Morpheel without equipping Zora Armor.",
|
||||
AchievementCategory::Glitched,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
void* dbgExists = fopAcM_SearchByName(fpcNm_B_MGN_e);
|
||||
if (dbgExists && AchievementSystem::get().hasSignal("open_letter")) {
|
||||
static bool prevMorpheelAlive = false;
|
||||
static bool inArena = false;
|
||||
static bool zoraWorn = false;
|
||||
const bool morpheelAlive = fopAcM_SearchByName(fpcNm_B_OH_e) != nullptr;
|
||||
const bool lakebedCleared = dComIfGs_isEventBit(dSv_event_flag_c::M_045);
|
||||
|
||||
if (morpheelAlive && !lakebedCleared) {
|
||||
inArena = true;
|
||||
if (daPy_py_c::checkZoraWearFlg()) {
|
||||
zoraWorn = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (prevMorpheelAlive && !morpheelAlive && inArena && !zoraWorn) {
|
||||
a.progress = 1;
|
||||
}
|
||||
|
||||
prevMorpheelAlive = morpheelAlive;
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"null_item",
|
||||
"Null Item",
|
||||
"Obtain the mysterious black rupee in the item wheel.",
|
||||
AchievementCategory::Glitched,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (daPy_getPlayerActorClass() == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 24; ++i) {
|
||||
if (dComIfGs_getItem(i, false) == 0x00) {
|
||||
a.progress = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"heavy-hitter",
|
||||
"Heavy Hitter",
|
||||
"Wear the Iron Boots during the end credits.",
|
||||
AchievementCategory::Misc,
|
||||
"stallord_skip",
|
||||
"Stallord Skip",
|
||||
"Leave Stallord's arena through the exit without defeating Stallord.",
|
||||
AchievementCategory::Glitched,
|
||||
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) {
|
||||
static bool seenStallord = false;
|
||||
if (strcmp(dComIfGp_getStartStageName(), "D_MN10A") != 0) {
|
||||
seenStallord = false;
|
||||
return;
|
||||
}
|
||||
if (daPy_getPlayerActorClass()->checkEquipHeavyBoots()) {
|
||||
if (dComIfGs_isEventBit(dSv_event_flag_c::F_0265)) {
|
||||
seenStallord = false;
|
||||
return;
|
||||
}
|
||||
if (fopAcM_SearchByName(fpcNm_B_DS_e) != nullptr) {
|
||||
seenStallord = true;
|
||||
}
|
||||
if (seenStallord &&
|
||||
dComIfGp_isEnableNextStage() &&
|
||||
strcmp(dComIfGp_getNextStageName(), "F_SP125") == 0) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user