diff --git a/include/dusk/achievements.h b/include/dusk/achievements.h index fb2da89c69..ca4676ab2d 100644 --- a/include/dusk/achievements.h +++ b/include/dusk/achievements.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include "nlohmann/json.hpp" @@ -14,6 +16,7 @@ enum class AchievementCategory : uint8_t { Collection, Challenge, Minigame, + Misc, Glitched }; @@ -42,6 +45,10 @@ public: 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 getAchievements() const; bool hasPendingUnlock() const { return !m_pendingUnlocks.empty(); } std::string consumePendingUnlock(); @@ -58,6 +65,7 @@ private: void processEntry(Entry& e); std::vector m_entries; + std::unordered_set m_signals; bool m_loaded = false; bool m_dirty = false; std::queue m_pendingUnlocks; diff --git a/src/d/actor/d_a_obj_carry.cpp b/src/d/actor/d_a_obj_carry.cpp index a8265f69f6..b83f9cd82f 100644 --- a/src/d/actor/d_a_obj_carry.cpp +++ b/src/d/actor/d_a_obj_carry.cpp @@ -21,6 +21,9 @@ #include "d/d_lib.h" #include "d/d_debug_viewer.h" #include "f_op/f_op_kankyo_mng.h" +#ifdef TARGET_PC +#include "dusk/achievements.h" +#endif static const cM3dGCylS l_cyl_info[] = { { 0.0f, 0.0f, 0.0f, 30.0f, 100.0f }, @@ -3582,6 +3585,12 @@ bool daObjCarry_c::cc_damage_proc_ironball() { } } +#ifdef TARGET_PC + if (mCannon && mCyl.ChkAtHit() && mCyl.GetAtHitAc() == daPy_getPlayerActorClass()) { + dusk::AchievementSystem::get().signal("iron_ball_hit_player"); + } +#endif + return var_r26; } diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index 4a7dac5e4d..58e99a3413 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -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 #include @@ -273,6 +274,21 @@ std::vector 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; + } + }, + {} + }, { { "back_in_time", @@ -441,6 +457,14 @@ 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) { @@ -485,6 +509,7 @@ void AchievementSystem::tick() { for (auto& e : m_entries) { processEntry(e); } + m_signals.clear(); if (m_dirty) { save(); m_dirty = false; diff --git a/src/dusk/imgui/ImGuiAchievements.cpp b/src/dusk/imgui/ImGuiAchievements.cpp index 479e307e30..113be5d95d 100644 --- a/src/dusk/imgui/ImGuiAchievements.cpp +++ b/src/dusk/imgui/ImGuiAchievements.cpp @@ -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)}, };