mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-25 10:52:19 -05:00
Gameplay Stat Tracker V1 (#1986)
* First test of gathering some gameplay stats * timer changes and other stuff * Move code to new files + rename * Name change - gamePlayStats * Finish rename, remove n64ddFlag checks * Improve item get times * Better time tracking, more stats, * Put button under Enhancements * Fix merge conflict * Add pauseCount, fix bug with rando items * Adjust inits/declarations * step counter * Name change: "itemGetTime" to "timestamp" * Tidying + CI test * Set up array for stat counts * Macro #define GAMEPLAYSTAT_TOTAL_TIME (gSaveContext.gameplayStats.playTimer / 2 + gSaveContext.gameplayStats.pauseTimer / 3) * Add boss defeat timestamps * Add sword swings, pots broken, bushes cut * fix int type * Add counts for enemies defeated Broken down by enemy, with a total * Add ammo used * Hide breakdowns until count > 0 * Forgot Big Octo * Count chests opened * Update after LUS submodule * Enemy count spacing * Comments * Count 3 mini Floormasters as 1 Floormaster + some cleanup * Comments * Colour coding for timestamps on quest items i.e. medallions/stones/songs * Move stat into the sohStats struct + rearrange the counts enum for easier addition of future counts * Some documentation + count button presses * Stop counting button presses when Ganon defeated * Couple bugfixes Add count for Gerudo Thief, fix step counter counting in some situations where it shouldn't * Fix comment
This commit is contained in:
parent
9cfe7bff47
commit
9c162fc0ec
@ -160,6 +160,7 @@ set(Header_Files__soh__Enhancements
|
||||
"soh/Enhancements/presets.h"
|
||||
"soh/Enhancements/savestates.h"
|
||||
"soh/Enhancements/savestates_extern.inc"
|
||||
"soh/Enhancements/gameplaystats.h"
|
||||
)
|
||||
source_group("Header Files\\soh\\Enhancements" FILES ${Header_Files__soh__Enhancements})
|
||||
|
||||
@ -290,6 +291,7 @@ set(Source_Files__soh__Enhancements
|
||||
"soh/Enhancements/gameconsole.c"
|
||||
"soh/Enhancements/presets.cpp"
|
||||
"soh/Enhancements/savestates.cpp"
|
||||
"soh/Enhancements/gameplaystats.cpp"
|
||||
)
|
||||
source_group("Source Files\\soh\\Enhancements" FILES ${Source_Files__soh__Enhancements})
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "macros.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
#include "soh/Enhancements/gameconsole.h"
|
||||
#include "soh/Enhancements/gameplaystats.h"
|
||||
#include <Cvar.h>
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "z64audio.h"
|
||||
#include "soh/Enhancements/randomizer/randomizerTypes.h"
|
||||
#include "soh/Enhancements/randomizer/randomizer_inf.h"
|
||||
#include "soh/Enhancements/gameplaystats.h"
|
||||
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
|
||||
|
||||
typedef struct {
|
||||
@ -30,6 +31,11 @@ typedef struct {
|
||||
/* */ u8 heartPieces;
|
||||
/* */ u8 heartContainers;
|
||||
/* */ u8 dungeonKeys[19];
|
||||
/* */ u32 playTimer;
|
||||
/* */ u32 pauseTimer;
|
||||
/* */ bool gameComplete;
|
||||
/* */ u32 timestamp[TIMESTAMP_MAX];
|
||||
/* */ u32 count[COUNT_MAX];
|
||||
} SohStats;
|
||||
|
||||
typedef struct {
|
||||
|
464
soh/soh/Enhancements/gameplaystats.cpp
Normal file
464
soh/soh/Enhancements/gameplaystats.cpp
Normal file
@ -0,0 +1,464 @@
|
||||
#include "gameplaystats.h"
|
||||
|
||||
#include "ImGuiImpl.h"
|
||||
#include "../UIWidgets.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <Cvar.h>
|
||||
#include <Hooks.h>
|
||||
|
||||
extern "C" {
|
||||
#include <z64.h>
|
||||
#include "variables.h"
|
||||
}
|
||||
|
||||
#define COLOR_WHITE ImVec4(1.00f, 1.00f, 1.00f, 1.00f)
|
||||
#define COLOR_RED ImVec4(1.00f, 0.00f, 0.00f, 1.00f)
|
||||
#define COLOR_GREEN ImVec4(0.10f, 1.00f, 0.10f, 1.00f)
|
||||
#define COLOR_BLUE ImVec4(0.00f, 0.33f, 1.00f, 1.00f)
|
||||
#define COLOR_PURPLE ImVec4(0.54f, 0.19f, 0.89f, 1.00f)
|
||||
#define COLOR_YELLOW ImVec4(1.00f, 1.00f, 0.00f, 1.00f)
|
||||
#define COLOR_ORANGE ImVec4(1.00f, 0.67f, 0.11f, 1.00f)
|
||||
#define COLOR_LIGHT_BLUE ImVec4(0.00f, 0.88f, 1.00f, 1.00f)
|
||||
#define COLOR_GREY ImVec4(0.78f, 0.78f, 0.78f, 1.00f)
|
||||
|
||||
char timestampDisplayName[TIMESTAMP_MAX][21] = { "" };
|
||||
ImVec4 timestampDisplayColor[TIMESTAMP_MAX];
|
||||
|
||||
typedef struct {
|
||||
char name[21];
|
||||
u32 time;
|
||||
ImVec4 color;
|
||||
}TimestampInfo;
|
||||
|
||||
// Timestamps are an array of structs, each with a name, time, and color
|
||||
// Names and colors are set up at the bottom of this file
|
||||
// Times are stored in gSaveContext.sohStats.timestamp
|
||||
TimestampInfo timestampDisplay[TIMESTAMP_MAX];
|
||||
|
||||
void DisplayTimeHHMMSS(uint32_t timeInTenthsOfSeconds, const char* text, ImVec4 color) {
|
||||
|
||||
uint32_t sec = timeInTenthsOfSeconds / 10;
|
||||
uint32_t hh = sec / 3600;
|
||||
uint32_t mm = (sec - hh * 3600) / 60;
|
||||
uint32_t ss = sec - hh * 3600 - mm * 60;
|
||||
uint32_t ds = timeInTenthsOfSeconds % 10;
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
|
||||
ImGui::Text(text);
|
||||
ImGui::SameLine();
|
||||
|
||||
// Hack to keep the timers aligned and prevent them from shifting around
|
||||
// Put a leading zero in front of the seconds or minutes if they're less than 10
|
||||
if (mm < 10 && ss < 10) {
|
||||
ImGui::Text("%u:0%u:0%u.%u", hh, mm, ss, ds);
|
||||
}
|
||||
if (mm < 10 && ss >= 10) {
|
||||
ImGui::Text("%u:0%u:%u.%u", hh, mm, ss, ds);
|
||||
}
|
||||
if (mm >= 10 && ss < 10) {
|
||||
ImGui::Text("%u:%u:0%u.%u", hh, mm, ss, ds);
|
||||
}
|
||||
if (mm >= 10 && ss >= 10) {
|
||||
ImGui::Text("%u:%u:%u.%u", hh, mm, ss, ds);
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void SortChronological(TimestampInfo* arr, size_t len) {
|
||||
TimestampInfo temp;
|
||||
for (int i = 0; i < len; i++) {
|
||||
for (int j = 0; j + 1 < len - i; j++) {
|
||||
if (arr[j].time > arr[j + 1].time) {
|
||||
temp = arr[j];
|
||||
arr[j] = arr[j + 1];
|
||||
arr[j + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayStat(const char* text, uint32_t value) {
|
||||
|
||||
ImGui::Text(text);
|
||||
ImGui::SameLine();
|
||||
// Hack to keep the digits properly aligned in the column
|
||||
if (value < 10) {
|
||||
ImGui::Text(" %u", value);
|
||||
} else if (value < 100) {
|
||||
ImGui::Text(" %u", value);
|
||||
} else if (value < 1000) {
|
||||
ImGui::Text(" %u", value);
|
||||
} else if (value < 10000) {
|
||||
ImGui::Text(" %u", value);
|
||||
} else if (value < 100000) {
|
||||
ImGui::Text(" %u", value);
|
||||
} else {
|
||||
ImGui::Text("%u", value);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayStatIfNonZero(const char* text, uint32_t value) {
|
||||
if (value > 0) {
|
||||
DisplayStat(text, value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void DrawStatsTracker(bool& open) {
|
||||
if (!open) {
|
||||
CVar_SetS32("gGameplayStatsEnabled", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(480, 550), ImGuiCond_Appearing);
|
||||
if (!ImGui::Begin("Gameplay Stats", &open, ImGuiWindowFlags_NoFocusOnAppearing)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
u32 totalTimer = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
u32 enemiesDefeated = 0;
|
||||
u32 ammoUsed = 0;
|
||||
u32 buttonPresses = 0;
|
||||
|
||||
// Sum of all enemies defeated
|
||||
for (int i = COUNT_ENEMIES_DEFEATED_ANUBIS; i <= COUNT_ENEMIES_DEFEATED_WOLFOS; i++) {
|
||||
if (i == COUNT_ENEMIES_DEFEATED_FLOORMASTER) {
|
||||
// Special case: You must kill 3 mini Floormasters for it count as one defeated Floormaster
|
||||
enemiesDefeated += gSaveContext.sohStats.count[i] / 3;
|
||||
} else {
|
||||
enemiesDefeated += gSaveContext.sohStats.count[i];
|
||||
}
|
||||
}
|
||||
// Sum of all ammo used
|
||||
for (int i = COUNT_AMMO_USED_STICK; i <= COUNT_AMMO_USED_BEAN; i++) {
|
||||
ammoUsed += gSaveContext.sohStats.count[i];
|
||||
}
|
||||
// Sum of all button presses
|
||||
for (int i = COUNT_BUTTON_PRESSES_A; i <= COUNT_BUTTON_PRESSES_START; i++) {
|
||||
buttonPresses += gSaveContext.sohStats.count[i];
|
||||
}
|
||||
// Set up the array of timestamps and then sort it chronologically
|
||||
for (int i = 0; i < TIMESTAMP_MAX; i++) {
|
||||
strcpy(timestampDisplay[i].name, timestampDisplayName[i]);
|
||||
timestampDisplay[i].time = gSaveContext.sohStats.timestamp[i];
|
||||
timestampDisplay[i].color = timestampDisplayColor[i];
|
||||
}
|
||||
SortChronological(timestampDisplay, sizeof(timestampDisplay) / sizeof(timestampDisplay[0]));
|
||||
|
||||
|
||||
// Begin drawing the table and showing the stats
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f });
|
||||
ImGui::BeginTable("timers", 1, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV);
|
||||
ImGui::TableSetupColumn("Timers", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
DisplayTimeHHMMSS(totalTimer, "Total Game Time: ", COLOR_WHITE);
|
||||
UIWidgets::Tooltip("Timer accuracy may be affected by game performance and loading.");
|
||||
DisplayTimeHHMMSS(gSaveContext.sohStats.playTimer / 2, "Gameplay Time: ", COLOR_WHITE);
|
||||
UIWidgets::Tooltip("Timer accuracy may be affected by game performance and loading.");
|
||||
DisplayTimeHHMMSS(gSaveContext.sohStats.pauseTimer / 3, "Pause Menu Time: ", COLOR_WHITE);
|
||||
|
||||
ImGui::PopStyleVar(1);
|
||||
ImGui::EndTable();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f });
|
||||
ImGui::BeginTable("gameStatsTable", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV);
|
||||
|
||||
ImGui::TableSetupColumn("Timestamps", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
||||
ImGui::TableSetupColumn("Counts", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
// Display chronological timestamps of items obtained and bosses defeated
|
||||
for (int i = 0; i < TIMESTAMP_MAX; i++) {
|
||||
// To be shown, the entry must have a non-zero time and a string for its display name
|
||||
if (timestampDisplay[i].time > 0 && strnlen(timestampDisplay[i].name, 21) > 1) {
|
||||
DisplayTimeHHMMSS(timestampDisplay[i].time, timestampDisplay[i].name, timestampDisplay[i].color);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
DisplayStat("Enemies Defeated: ", enemiesDefeated);
|
||||
// Show breakdown of enemies defeated in a tree. Only show counts for enemies if they've been defeated at least once.
|
||||
if (enemiesDefeated > 0) {
|
||||
if (ImGui::TreeNode("Enemy Details...")) {
|
||||
|
||||
DisplayStatIfNonZero("Anubis: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ANUBIS]);
|
||||
DisplayStatIfNonZero("Armos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ARMOS]);
|
||||
DisplayStatIfNonZero("Bari: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BARI]);
|
||||
DisplayStatIfNonZero("Biri: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BIRI]);
|
||||
DisplayStatIfNonZero("Beamos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BEAMOS]);
|
||||
DisplayStatIfNonZero("Big Octo: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BIG_OCTO]);
|
||||
DisplayStatIfNonZero("Bubble (Blue): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_BLUE]);
|
||||
DisplayStatIfNonZero("Bubble (Green): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_GREEN]);
|
||||
DisplayStatIfNonZero("Bubble (Red): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_RED]);
|
||||
DisplayStatIfNonZero("Bubble (White): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_WHITE]);
|
||||
DisplayStatIfNonZero("Business Scrub: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUSINESS_SCRUB]);
|
||||
DisplayStatIfNonZero("Dark Link: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DARK_LINK]);
|
||||
DisplayStatIfNonZero("Dead Hand: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEAD_HAND]);
|
||||
DisplayStatIfNonZero("Deku Baba: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEKU_BABA]);
|
||||
DisplayStatIfNonZero("Deku Baba (Big): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEKU_BABA_BIG]);
|
||||
DisplayStatIfNonZero("Deku Scrub: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEKU_SCRUB]);
|
||||
DisplayStatIfNonZero("Dinolfos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DINOLFOS]);
|
||||
DisplayStatIfNonZero("Dodongo: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DODONGO]);
|
||||
DisplayStatIfNonZero("Dodongo (Baby): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DODONGO_BABY]);
|
||||
DisplayStatIfNonZero("Door Mimic: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DOOR_TRAP]);
|
||||
DisplayStatIfNonZero("Flare Dancer: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLARE_DANCER]);
|
||||
DisplayStatIfNonZero("Floormaster: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLOORMASTER]/3);
|
||||
DisplayStatIfNonZero("Flying Floor Tile: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLOOR_TILE]);
|
||||
DisplayStatIfNonZero("Flying Pot: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLYING_POT]);
|
||||
DisplayStatIfNonZero("Freezard: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FREEZARD]);
|
||||
DisplayStatIfNonZero("Gerudo Thief: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GERUDO_THIEF]);
|
||||
DisplayStatIfNonZero("Gibdo: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GIBDO]);
|
||||
DisplayStatIfNonZero("Gohma Larva: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GOHMA_LARVA]);
|
||||
DisplayStatIfNonZero("Guay: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GUAY]);
|
||||
DisplayStatIfNonZero("Iron Knuckle: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE]);
|
||||
DisplayStatIfNonZero("Iron Knuckle (Nab): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE_NABOORU]);
|
||||
DisplayStatIfNonZero("Keese: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_KEESE]);
|
||||
DisplayStatIfNonZero("Keese (Fire): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_KEESE_FIRE]);
|
||||
DisplayStatIfNonZero("Keese (Ice): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_KEESE_ICE]);
|
||||
DisplayStatIfNonZero("Leever: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LEEVER]);
|
||||
DisplayStatIfNonZero("Leever (Big): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LEEVER_BIG]);
|
||||
DisplayStatIfNonZero("Like-Like: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LIKE_LIKE]);
|
||||
DisplayStatIfNonZero("Lizalfos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LIZALFOS]);
|
||||
DisplayStatIfNonZero("Mad Scrub: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_MAD_SCRUB]);
|
||||
DisplayStatIfNonZero("Moblin: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_MOBLIN]);
|
||||
DisplayStatIfNonZero("Moblin (Club): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_MOBLIN_CLUB]);
|
||||
DisplayStatIfNonZero("Octorok: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_OCTOROK]);
|
||||
DisplayStatIfNonZero("Parasitic Tentacle: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_PARASITIC_TENTACLE]);
|
||||
DisplayStatIfNonZero("Peahat: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_PEAHAT]);
|
||||
DisplayStatIfNonZero("Peahat Larva: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_PEAHAT_LARVA]);
|
||||
DisplayStatIfNonZero("Poe: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE]);
|
||||
DisplayStatIfNonZero("Poe (Big): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE_BIG]);
|
||||
DisplayStatIfNonZero("Poe (Composer): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE_COMPOSER]);
|
||||
DisplayStatIfNonZero("Poe Sisters: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE_SISTERS]);
|
||||
DisplayStatIfNonZero("Redead: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_REDEAD]);
|
||||
DisplayStatIfNonZero("Shabom: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SHABOM]);
|
||||
DisplayStatIfNonZero("Shellblade: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SHELLBLADE]);
|
||||
DisplayStatIfNonZero("Skull Kid: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULL_KID]);
|
||||
DisplayStatIfNonZero("Skulltula: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULLTULA]);
|
||||
DisplayStatIfNonZero("Skulltula (Big): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULLTULA_BIG]);
|
||||
DisplayStatIfNonZero("Skulltula (Gold): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULLTULA_GOLD]);
|
||||
DisplayStatIfNonZero("Skullwalltula: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULLWALLTULA]);
|
||||
DisplayStatIfNonZero("Spike: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SPIKE]);
|
||||
DisplayStatIfNonZero("Stalchild: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STALCHILD]);
|
||||
DisplayStatIfNonZero("Stalfos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STALFOS]);
|
||||
DisplayStatIfNonZero("Stinger: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STINGER]);
|
||||
DisplayStatIfNonZero("Tailpasaran: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_TAILPASARAN]);
|
||||
DisplayStatIfNonZero("Tektite (Blue): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_TEKTITE_BLUE]);
|
||||
DisplayStatIfNonZero("Tektite (Red): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_TEKTITE_RED]);
|
||||
DisplayStatIfNonZero("Torch Slug: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_TORCH_SLUG]);
|
||||
DisplayStatIfNonZero("Wallmaster: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WALLMASTER]);
|
||||
DisplayStatIfNonZero("Withered Deku Baba: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WITHERED_DEKU_BABA]);
|
||||
DisplayStatIfNonZero("Wolfos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WOLFOS]);
|
||||
DisplayStatIfNonZero("Wolfos (White): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WOLFOS_WHITE]);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
DisplayStat("Rupees Collected: ", gSaveContext.sohStats.count[COUNT_RUPEES_COLLECTED]);
|
||||
UIWidgets::Tooltip("Includes rupees collected with a full wallet.");
|
||||
DisplayStat("Rupees Spent: ", gSaveContext.sohStats.count[COUNT_RUPEES_SPENT]);
|
||||
DisplayStat("Chests Opened: ", gSaveContext.sohStats.count[COUNT_CHESTS_OPENED]);
|
||||
|
||||
DisplayStat("Ammo Used: ", ammoUsed);
|
||||
// Show breakdown of ammo used in a collapsible tree. Only show ammo types if they've been used at least once.
|
||||
if (ammoUsed > 0) {
|
||||
if (ImGui::TreeNode("Ammo Details...")) {
|
||||
|
||||
DisplayStatIfNonZero("Deku Sticks: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_STICK]);
|
||||
DisplayStatIfNonZero("Deku Nuts: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_NUT]);
|
||||
DisplayStatIfNonZero("Deku Seeds: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_SEED]);
|
||||
DisplayStatIfNonZero("Bombs: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_BOMB]);
|
||||
DisplayStatIfNonZero("Bombchus: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_BOMBCHU]);
|
||||
DisplayStatIfNonZero("Arrows: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_ARROW]);
|
||||
DisplayStatIfNonZero("Beans: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_BEAN]);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
DisplayStat("Damage Taken: ", gSaveContext.sohStats.count[COUNT_DAMAGE_TAKEN]);
|
||||
DisplayStat("Sword Swings: ", gSaveContext.sohStats.count[COUNT_SWORD_SWINGS]);
|
||||
DisplayStat("Steps Taken: ", gSaveContext.sohStats.count[COUNT_STEPS]);
|
||||
DisplayStat("Rolls: ", gSaveContext.sohStats.count[COUNT_ROLLS]);
|
||||
DisplayStat("Bonks: ", gSaveContext.sohStats.count[COUNT_BONKS]);
|
||||
DisplayStat("Ice Traps: ", gSaveContext.sohStats.count[COUNT_ICE_TRAPS]);
|
||||
DisplayStat("Pauses: ", gSaveContext.sohStats.count[COUNT_PAUSES]);
|
||||
DisplayStat("Pots Smashed: ", gSaveContext.sohStats.count[COUNT_POTS_BROKEN]);
|
||||
DisplayStat("Bushes Cut: ", gSaveContext.sohStats.count[COUNT_BUSHES_CUT]);
|
||||
|
||||
DisplayStat("Buttons Pressed: ", buttonPresses);
|
||||
// Show breakdown of ammo used in a collapsible tree. Only show ammo types if they've been used at least once.
|
||||
if (buttonPresses > 0) {
|
||||
if (ImGui::TreeNode("Buttons...")) {
|
||||
|
||||
DisplayStatIfNonZero("A: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_A]);
|
||||
DisplayStatIfNonZero("B: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_B]);
|
||||
DisplayStatIfNonZero("L: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_L]);
|
||||
DisplayStatIfNonZero("R: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_R]);
|
||||
DisplayStatIfNonZero("Z: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_Z]);
|
||||
DisplayStatIfNonZero("C-Up: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CUP]);
|
||||
DisplayStatIfNonZero("C-Right: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CRIGHT]);
|
||||
DisplayStatIfNonZero("C-Down: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CDOWN]);
|
||||
DisplayStatIfNonZero("C-Left: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CLEFT]);
|
||||
DisplayStatIfNonZero("D-Up: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DUP]);
|
||||
DisplayStatIfNonZero("D-Right: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DRIGHT]);
|
||||
DisplayStatIfNonZero("D-Down: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DDOWN]);
|
||||
DisplayStatIfNonZero("D-Left: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DLEFT]);
|
||||
DisplayStatIfNonZero("Start: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_START]);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(1);
|
||||
ImGui::EndTable();
|
||||
|
||||
ImGui::Text("Note: Gameplay stats are saved to the current file and will be\nlost if you quit without saving.");
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Entries listed here will have a timestamp shown in the stat window
|
||||
void SetupDisplayNames() {
|
||||
// To add a timestamp for an item or event, add it to this list and ensure
|
||||
// it has a corresponding entry in the enum (see gameplaystats.h)
|
||||
|
||||
strcpy(timestampDisplayName[ITEM_BOW], "Fairy Bow: ");
|
||||
strcpy(timestampDisplayName[ITEM_ARROW_FIRE], "Fire Arrows: ");
|
||||
strcpy(timestampDisplayName[ITEM_DINS_FIRE], "Din's Fire: ");
|
||||
strcpy(timestampDisplayName[ITEM_SLINGSHOT], "Slingshot: ");
|
||||
strcpy(timestampDisplayName[ITEM_OCARINA_FAIRY], "Fairy Ocarina: ");
|
||||
strcpy(timestampDisplayName[ITEM_OCARINA_TIME], "Ocarina of Time: ");
|
||||
strcpy(timestampDisplayName[ITEM_BOMBCHU], "Bombchus: ");
|
||||
strcpy(timestampDisplayName[ITEM_HOOKSHOT], "Hookshot: ");
|
||||
strcpy(timestampDisplayName[ITEM_LONGSHOT], "Longshot: ");
|
||||
strcpy(timestampDisplayName[ITEM_ARROW_ICE], "Ice Arrows: ");
|
||||
strcpy(timestampDisplayName[ITEM_FARORES_WIND], "Farore's Wind: ");
|
||||
strcpy(timestampDisplayName[ITEM_BOOMERANG], "Boomerang: ");
|
||||
strcpy(timestampDisplayName[ITEM_LENS], "Lens of Truth: ");
|
||||
strcpy(timestampDisplayName[ITEM_HAMMER], "Megaton Hammer: ");
|
||||
strcpy(timestampDisplayName[ITEM_ARROW_LIGHT], "Light Arrows: ");
|
||||
strcpy(timestampDisplayName[ITEM_BOTTLE], "Bottle: ");
|
||||
strcpy(timestampDisplayName[ITEM_LETTER_ZELDA], "Zelda's Letter: ");
|
||||
strcpy(timestampDisplayName[ITEM_SWORD_KOKIRI], "Kokiri Sword: ");
|
||||
strcpy(timestampDisplayName[ITEM_SWORD_MASTER], "Master Sword: ");
|
||||
strcpy(timestampDisplayName[ITEM_SWORD_BGS], "Biggoron's Sword: ");
|
||||
strcpy(timestampDisplayName[ITEM_SHIELD_DEKU], "Deku Shield: ");
|
||||
strcpy(timestampDisplayName[ITEM_SHIELD_HYLIAN], "Hylian Shield: ");
|
||||
strcpy(timestampDisplayName[ITEM_SHIELD_MIRROR], "Mirror Shield: ");
|
||||
strcpy(timestampDisplayName[ITEM_TUNIC_GORON], "Goron Tunic: ");
|
||||
strcpy(timestampDisplayName[ITEM_TUNIC_ZORA], "Zora Tunic: ");
|
||||
strcpy(timestampDisplayName[ITEM_BOOTS_IRON], "Iron Boots: ");
|
||||
strcpy(timestampDisplayName[ITEM_BOOTS_HOVER], "Hover Boots: ");
|
||||
strcpy(timestampDisplayName[ITEM_BOMB_BAG_20], "Bomb Bag: ");
|
||||
strcpy(timestampDisplayName[ITEM_BRACELET], "Goron's Bracelet: ");
|
||||
strcpy(timestampDisplayName[ITEM_GAUNTLETS_SILVER], "Silver Gauntlets: ");
|
||||
strcpy(timestampDisplayName[ITEM_GAUNTLETS_GOLD], "Gold Gauntlets: ");
|
||||
strcpy(timestampDisplayName[ITEM_SCALE_SILVER], "Silver Scale: ");
|
||||
strcpy(timestampDisplayName[ITEM_SCALE_GOLDEN], "Gold Scale: ");
|
||||
strcpy(timestampDisplayName[ITEM_WALLET_ADULT], "Adult's Wallet: ");
|
||||
strcpy(timestampDisplayName[ITEM_WALLET_GIANT], "Giant's Wallet: ");
|
||||
strcpy(timestampDisplayName[ITEM_SONG_MINUET], "Minuet of Forest: ");
|
||||
strcpy(timestampDisplayName[ITEM_SONG_BOLERO], "Bolero of Fire: ");
|
||||
strcpy(timestampDisplayName[ITEM_SONG_SERENADE], "Serenade of Water: ");
|
||||
strcpy(timestampDisplayName[ITEM_SONG_REQUIEM], "Requiem of Spirit: ");
|
||||
strcpy(timestampDisplayName[ITEM_SONG_NOCTURNE], "Nocturne of Shadow: ");
|
||||
strcpy(timestampDisplayName[ITEM_SONG_PRELUDE], "Prelude of Light: ");
|
||||
strcpy(timestampDisplayName[ITEM_SONG_LULLABY], "Zelda's Lullaby: ");
|
||||
strcpy(timestampDisplayName[ITEM_SONG_EPONA], "Epona's Song: ");
|
||||
strcpy(timestampDisplayName[ITEM_SONG_SARIA], "Saria's Song: ");
|
||||
strcpy(timestampDisplayName[ITEM_SONG_SUN], "Sun's Song: ");
|
||||
strcpy(timestampDisplayName[ITEM_SONG_TIME], "Song of Time: ");
|
||||
strcpy(timestampDisplayName[ITEM_SONG_STORMS], "Song of Storms: ");
|
||||
strcpy(timestampDisplayName[ITEM_MEDALLION_FOREST], "Forest Medallion: ");
|
||||
strcpy(timestampDisplayName[ITEM_MEDALLION_FIRE], "Fire Medallion: ");
|
||||
strcpy(timestampDisplayName[ITEM_MEDALLION_WATER], "Water Medallion: ");
|
||||
strcpy(timestampDisplayName[ITEM_MEDALLION_SPIRIT], "Spirit Medallion: ");
|
||||
strcpy(timestampDisplayName[ITEM_MEDALLION_SHADOW], "Shadow Medallion: ");
|
||||
strcpy(timestampDisplayName[ITEM_MEDALLION_LIGHT], "Light Medallion: ");
|
||||
strcpy(timestampDisplayName[ITEM_KOKIRI_EMERALD], "Kokiri's Emerald: ");
|
||||
strcpy(timestampDisplayName[ITEM_GORON_RUBY], "Goron's Ruby: ");
|
||||
strcpy(timestampDisplayName[ITEM_ZORA_SAPPHIRE], "Zora's Sapphire: ");
|
||||
strcpy(timestampDisplayName[ITEM_SINGLE_MAGIC], "Magic: ");
|
||||
strcpy(timestampDisplayName[ITEM_DOUBLE_DEFENSE], "Double Defense: ");
|
||||
|
||||
// Other events
|
||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_GOHMA], "Gohma Defeated: ");
|
||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_KING_DODONGO], "KD Defeated: ");
|
||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_BARINADE], "Barinade Defeated: ");
|
||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_PHANTOM_GANON], "PG Defeated: ");
|
||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_VOLVAGIA], "Volvagia Defeated: ");
|
||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_MORPHA], "Morpha Defeated: ");
|
||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_BONGO_BONGO], "Bongo Defeated: ");
|
||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_TWINROVA], "Twinrova Defeated: ");
|
||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_GANONDORF], "Ganondorf Defeated: ");
|
||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_GANON], "Ganon Defeated: ");
|
||||
}
|
||||
|
||||
void SetupDisplayColors() {
|
||||
for (int i = 0; i < TIMESTAMP_MAX; i++) {
|
||||
switch (i) {
|
||||
case ITEM_SONG_MINUET:
|
||||
case ITEM_KOKIRI_EMERALD:
|
||||
case ITEM_SONG_SARIA:
|
||||
case ITEM_MEDALLION_FOREST:
|
||||
timestampDisplayColor[i] = COLOR_GREEN;
|
||||
break;
|
||||
case ITEM_SONG_BOLERO:
|
||||
case ITEM_GORON_RUBY:
|
||||
case ITEM_MEDALLION_FIRE:
|
||||
timestampDisplayColor[i] = COLOR_RED;
|
||||
break;
|
||||
case ITEM_SONG_SERENADE:
|
||||
case ITEM_ZORA_SAPPHIRE:
|
||||
case ITEM_MEDALLION_WATER:
|
||||
timestampDisplayColor[i] = COLOR_BLUE;
|
||||
break;
|
||||
case ITEM_SONG_LULLABY:
|
||||
case ITEM_SONG_NOCTURNE:
|
||||
case ITEM_MEDALLION_SHADOW:
|
||||
timestampDisplayColor[i] = COLOR_PURPLE;
|
||||
break;
|
||||
case ITEM_SONG_EPONA:
|
||||
case ITEM_SONG_REQUIEM:
|
||||
case ITEM_MEDALLION_SPIRIT:
|
||||
timestampDisplayColor[i] = COLOR_ORANGE;
|
||||
break;
|
||||
case ITEM_SONG_SUN:
|
||||
case ITEM_SONG_PRELUDE:
|
||||
case ITEM_MEDALLION_LIGHT:
|
||||
case ITEM_ARROW_LIGHT:
|
||||
timestampDisplayColor[i] = COLOR_YELLOW;
|
||||
break;
|
||||
case ITEM_SONG_STORMS:
|
||||
timestampDisplayColor[i] = COLOR_GREY;
|
||||
break;
|
||||
case ITEM_SONG_TIME:
|
||||
timestampDisplayColor[i] = COLOR_LIGHT_BLUE;
|
||||
break;
|
||||
default:
|
||||
timestampDisplayColor[i] = COLOR_WHITE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitStatTracker() {
|
||||
SohImGui::AddWindow("Enhancements", "Gameplay Stats", DrawStatsTracker);
|
||||
SetupDisplayNames();
|
||||
SetupDisplayColors();
|
||||
}
|
138
soh/soh/Enhancements/gameplaystats.h
Normal file
138
soh/soh/Enhancements/gameplaystats.h
Normal file
@ -0,0 +1,138 @@
|
||||
#pragma once
|
||||
|
||||
// Total gameplay time is tracked in tenths of seconds
|
||||
// I.E. game time counts frames at 20fps/2, pause time counts frames at 30fps/3
|
||||
// Frame counts in z_play.c and z_kaleido_scope_call.c
|
||||
#define GAMEPLAYSTAT_TOTAL_TIME (gSaveContext.sohStats.playTimer / 2 + gSaveContext.sohStats.pauseTimer / 3)
|
||||
|
||||
void InitStatTracker();
|
||||
|
||||
typedef enum {
|
||||
// 0x00 to 0x9B (0 to 155) used for getting items,
|
||||
// piggybacked off enum "ItemID" in z64item.h
|
||||
|
||||
/* 0xA0 */ TIMESTAMP_DEFEAT_GOHMA = 0xA0, // z_boss_goma.c
|
||||
/* 0xA1 */ TIMESTAMP_DEFEAT_KING_DODONGO, // z_boss_dodongo.c
|
||||
/* 0xA2 */ TIMESTAMP_DEFEAT_BARINADE, // z_boss_va.c
|
||||
/* 0xA3 */ TIMESTAMP_DEFEAT_PHANTOM_GANON, // z_boss_ganondrof.c
|
||||
/* 0xA4 */ TIMESTAMP_DEFEAT_VOLVAGIA, // z_boss_fd2.c
|
||||
/* 0xA5 */ TIMESTAMP_DEFEAT_MORPHA, // z_boss_mo.c
|
||||
/* 0xA6 */ TIMESTAMP_DEFEAT_BONGO_BONGO, // z_boss_sst.c
|
||||
/* 0xA7 */ TIMESTAMP_DEFEAT_TWINROVA, // z_boss_tw.c
|
||||
/* 0xA8 */ TIMESTAMP_DEFEAT_GANONDORF, // z_boss_ganon.c
|
||||
/* 0xA9 */ TIMESTAMP_DEFEAT_GANON, // z_boss_ganon2.c
|
||||
/* 0xAA */ TIMESTAMP_MAX
|
||||
|
||||
}GameplayStatTimestamp;
|
||||
|
||||
typedef enum {
|
||||
// Enemies defeated
|
||||
COUNT_ENEMIES_DEFEATED_ANUBIS, // EN_ANUBICE
|
||||
COUNT_ENEMIES_DEFEATED_ARMOS, // EN_AM
|
||||
COUNT_ENEMIES_DEFEATED_BARI, // EN_VALI
|
||||
COUNT_ENEMIES_DEFEATED_BEAMOS, // EN_VM
|
||||
COUNT_ENEMIES_DEFEATED_BIG_OCTO, // EN_BIGOKUTA
|
||||
COUNT_ENEMIES_DEFEATED_BIRI, // EN_BILI
|
||||
COUNT_ENEMIES_DEFEATED_BUBBLE_GREEN, // EN_BB
|
||||
COUNT_ENEMIES_DEFEATED_BUBBLE_BLUE, // EN_BB
|
||||
COUNT_ENEMIES_DEFEATED_BUBBLE_WHITE, // EN_BB
|
||||
COUNT_ENEMIES_DEFEATED_BUBBLE_RED, // EN_BB
|
||||
COUNT_ENEMIES_DEFEATED_BUSINESS_SCRUB, // EN_DNS
|
||||
COUNT_ENEMIES_DEFEATED_DARK_LINK, // EN_TORCH2
|
||||
COUNT_ENEMIES_DEFEATED_DEAD_HAND, // EN_DH
|
||||
COUNT_ENEMIES_DEFEATED_DEKU_BABA, // EN_DEKUBABA
|
||||
COUNT_ENEMIES_DEFEATED_DEKU_BABA_BIG, // EN_DEKUBABA
|
||||
COUNT_ENEMIES_DEFEATED_DEKU_SCRUB, // EN_HINTNUTS
|
||||
COUNT_ENEMIES_DEFEATED_DINOLFOS, // EN_ZF
|
||||
COUNT_ENEMIES_DEFEATED_DODONGO, // EN_DODONGO
|
||||
COUNT_ENEMIES_DEFEATED_DODONGO_BABY, // EN_DODOJR
|
||||
COUNT_ENEMIES_DEFEATED_DOOR_TRAP, // DOOR_KILLER
|
||||
COUNT_ENEMIES_DEFEATED_FLARE_DANCER, // EN_FD
|
||||
COUNT_ENEMIES_DEFEATED_FLOORMASTER, // EN_FLOORMAS
|
||||
COUNT_ENEMIES_DEFEATED_FLYING_POT, // EN_TUBO_TRAP
|
||||
COUNT_ENEMIES_DEFEATED_FLOOR_TILE, // EN_YUKABYUN
|
||||
COUNT_ENEMIES_DEFEATED_FREEZARD, // EN_FZ
|
||||
COUNT_ENEMIES_DEFEATED_GERUDO_THIEF, // EN_GELDB
|
||||
COUNT_ENEMIES_DEFEATED_GIBDO, // EN_RD
|
||||
COUNT_ENEMIES_DEFEATED_GOHMA_LARVA, // EN_GOMA
|
||||
COUNT_ENEMIES_DEFEATED_GUAY, // EN_CROW
|
||||
COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE, // EN_IK
|
||||
COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE_NABOORU, // EN_IK
|
||||
COUNT_ENEMIES_DEFEATED_KEESE, // EN_FIREFLY
|
||||
COUNT_ENEMIES_DEFEATED_KEESE_FIRE, // EN_FIREFLY
|
||||
COUNT_ENEMIES_DEFEATED_KEESE_ICE, // EN_FIREFLY
|
||||
COUNT_ENEMIES_DEFEATED_LEEVER, // EN_REEBA
|
||||
COUNT_ENEMIES_DEFEATED_LEEVER_BIG, // EN_REEBA
|
||||
COUNT_ENEMIES_DEFEATED_LIKE_LIKE, // EN_RR
|
||||
COUNT_ENEMIES_DEFEATED_LIZALFOS, // EN_ZF
|
||||
COUNT_ENEMIES_DEFEATED_MAD_SCRUB, // EN_DEKUNUTS
|
||||
COUNT_ENEMIES_DEFEATED_MOBLIN, // EN_MB
|
||||
COUNT_ENEMIES_DEFEATED_MOBLIN_CLUB, // EN_MB
|
||||
COUNT_ENEMIES_DEFEATED_OCTOROK, // EN_OKUTA
|
||||
COUNT_ENEMIES_DEFEATED_PARASITIC_TENTACLE, // EN_BA
|
||||
COUNT_ENEMIES_DEFEATED_PEAHAT, // EN_PEEHAT
|
||||
COUNT_ENEMIES_DEFEATED_PEAHAT_LARVA, // EN_PEEHAT
|
||||
COUNT_ENEMIES_DEFEATED_POE, // EN_POH
|
||||
COUNT_ENEMIES_DEFEATED_POE_BIG, // EN_PO_FIELD
|
||||
COUNT_ENEMIES_DEFEATED_POE_COMPOSER, // EN_POH
|
||||
COUNT_ENEMIES_DEFEATED_POE_SISTERS, // EN_PO_SISTERS
|
||||
COUNT_ENEMIES_DEFEATED_REDEAD, // EN_RD
|
||||
COUNT_ENEMIES_DEFEATED_SHABOM, // EN_BUBBLE
|
||||
COUNT_ENEMIES_DEFEATED_SHELLBLADE, // EN_SB
|
||||
COUNT_ENEMIES_DEFEATED_SKULLTULA, // EN_ST
|
||||
COUNT_ENEMIES_DEFEATED_SKULLTULA_BIG, // EN_ST
|
||||
COUNT_ENEMIES_DEFEATED_SKULLTULA_GOLD, // EN_SW
|
||||
COUNT_ENEMIES_DEFEATED_SKULLWALLTULA, // EN_SW
|
||||
COUNT_ENEMIES_DEFEATED_SKULL_KID, // EN_SKJ
|
||||
COUNT_ENEMIES_DEFEATED_SPIKE, // EN_NY
|
||||
COUNT_ENEMIES_DEFEATED_STALCHILD, // EN_SKB
|
||||
COUNT_ENEMIES_DEFEATED_STALFOS, // EN_TEST
|
||||
COUNT_ENEMIES_DEFEATED_STINGER, // EN_WEIYER
|
||||
COUNT_ENEMIES_DEFEATED_TAILPASARAN, // EN_TP
|
||||
COUNT_ENEMIES_DEFEATED_TEKTITE_BLUE, // EN_TITE
|
||||
COUNT_ENEMIES_DEFEATED_TEKTITE_RED, // EN_TITE
|
||||
COUNT_ENEMIES_DEFEATED_TORCH_SLUG, // EN_BW
|
||||
COUNT_ENEMIES_DEFEATED_WALLMASTER, // EN_WALLMAS
|
||||
COUNT_ENEMIES_DEFEATED_WITHERED_DEKU_BABA, // EN_KAREBABA
|
||||
COUNT_ENEMIES_DEFEATED_WOLFOS, // EN_WF
|
||||
COUNT_ENEMIES_DEFEATED_WOLFOS_WHITE, // EN_WF
|
||||
// Ammo used (z_parameter.c)
|
||||
COUNT_AMMO_USED_STICK,
|
||||
COUNT_AMMO_USED_NUT,
|
||||
COUNT_AMMO_USED_BOMB,
|
||||
COUNT_AMMO_USED_ARROW,
|
||||
COUNT_AMMO_USED_SEED,
|
||||
COUNT_AMMO_USED_BOMBCHU,
|
||||
COUNT_AMMO_USED_BEAN,
|
||||
// Buttons pressed (z_play.c)
|
||||
COUNT_BUTTON_PRESSES_A,
|
||||
COUNT_BUTTON_PRESSES_B,
|
||||
COUNT_BUTTON_PRESSES_L,
|
||||
COUNT_BUTTON_PRESSES_R,
|
||||
COUNT_BUTTON_PRESSES_Z,
|
||||
COUNT_BUTTON_PRESSES_CUP,
|
||||
COUNT_BUTTON_PRESSES_CRIGHT,
|
||||
COUNT_BUTTON_PRESSES_CDOWN,
|
||||
COUNT_BUTTON_PRESSES_CLEFT,
|
||||
COUNT_BUTTON_PRESSES_DUP,
|
||||
COUNT_BUTTON_PRESSES_DRIGHT,
|
||||
COUNT_BUTTON_PRESSES_DDOWN,
|
||||
COUNT_BUTTON_PRESSES_DLEFT,
|
||||
COUNT_BUTTON_PRESSES_START,
|
||||
// Other counts
|
||||
COUNT_RUPEES_COLLECTED, // z_parameter.c
|
||||
COUNT_RUPEES_SPENT, // z_parameter.c
|
||||
COUNT_CHESTS_OPENED, // z_en_box.c
|
||||
COUNT_DAMAGE_TAKEN, // z_parameter.c
|
||||
COUNT_ICE_TRAPS, // z_player.c
|
||||
COUNT_ROLLS, // z_player.c
|
||||
COUNT_BONKS, // z_player.c
|
||||
COUNT_PAUSES, // z_kaleido_scope_call.c
|
||||
COUNT_STEPS, // z_player.c
|
||||
COUNT_POTS_BROKEN, // z_obj_tsubo.c
|
||||
COUNT_BUSHES_CUT, // z_en_kusa.c
|
||||
COUNT_SWORD_SWINGS, // z_player.c
|
||||
|
||||
COUNT_MAX
|
||||
|
||||
} GameplayStatCount;
|
@ -802,6 +802,12 @@ namespace GameMenuBar {
|
||||
SohImGui::RequestCvarSaveOnNextTick();
|
||||
SohImGui::EnableWindow("SFX Editor", CVar_GetS32("gSfxEditor", 0));
|
||||
}
|
||||
if (ImGui::Button(GetWindowButtonText("Gameplay Stats", CVar_GetS32("gGameplayStatsEnabled", 0)).c_str(), ImVec2(-1.0f, 0.0f))) {
|
||||
bool currentValue = CVar_GetS32("gGameplayStatsEnabled", 0);
|
||||
CVar_SetS32("gGameplayStatsEnabled", !currentValue);
|
||||
SohImGui::RequestCvarSaveOnNextTick();
|
||||
SohImGui::EnableWindow("Gameplay Stats", CVar_GetS32("gGameplayStatsEnabled", 0));
|
||||
}
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor(1);
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "Enhancements/randomizer/randomizer.h"
|
||||
#include "Enhancements/randomizer/randomizer_item_tracker.h"
|
||||
#include "Enhancements/randomizer/3drando/random.hpp"
|
||||
#include "Enhancements/gameplaystats.h"
|
||||
#include "Enhancements/n64_weird_frame_data.inc"
|
||||
#include "frame_interpolation.h"
|
||||
#include "variables.h"
|
||||
@ -438,6 +439,7 @@ extern "C" void InitOTR() {
|
||||
Debug_Init();
|
||||
Rando_Init();
|
||||
InitItemTracker();
|
||||
InitStatTracker();
|
||||
OTRExtScanner();
|
||||
VanillaItemTable_Init();
|
||||
|
||||
|
@ -439,6 +439,15 @@ void SaveManager::InitFileNormal() {
|
||||
for (int dungeon = 0; dungeon < ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys); dungeon++) {
|
||||
gSaveContext.sohStats.dungeonKeys[dungeon] = 0;
|
||||
}
|
||||
gSaveContext.sohStats.playTimer = 0;
|
||||
gSaveContext.sohStats.pauseTimer = 0;
|
||||
for (int timestamp = 0; timestamp < ARRAY_COUNT(gSaveContext.sohStats.timestamp); timestamp++) {
|
||||
gSaveContext.sohStats.timestamp[timestamp] = 0;
|
||||
}
|
||||
for (int count = 0; count < ARRAY_COUNT(gSaveContext.sohStats.count); count++) {
|
||||
gSaveContext.sohStats.count[count] = 0;
|
||||
}
|
||||
gSaveContext.sohStats.gameComplete = false;
|
||||
for (int scene = 0; scene < ARRAY_COUNT(gSaveContext.sceneFlags); scene++) {
|
||||
gSaveContext.sceneFlags[scene].chest = 0;
|
||||
gSaveContext.sceneFlags[scene].swch = 0;
|
||||
@ -989,6 +998,14 @@ void SaveManager::LoadBaseVersion2() {
|
||||
SaveManager::Instance->LoadArray("dungeonKeys", ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.dungeonKeys[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadData("playTimer", gSaveContext.sohStats.playTimer);
|
||||
SaveManager::Instance->LoadData("pauseTimer", gSaveContext.sohStats.pauseTimer);
|
||||
SaveManager::Instance->LoadArray("timestamps", ARRAY_COUNT(gSaveContext.sohStats.timestamp), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.timestamp[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("counts", ARRAY_COUNT(gSaveContext.sohStats.count), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.count[i]);
|
||||
});
|
||||
});
|
||||
SaveManager::Instance->LoadArray("sceneFlags", ARRAY_COUNT(gSaveContext.sceneFlags), [](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&i]() {
|
||||
@ -1150,6 +1167,14 @@ void SaveManager::SaveBase() {
|
||||
SaveManager::Instance->SaveArray("dungeonKeys", ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys), [](size_t i) {
|
||||
SaveManager::Instance->SaveData("", gSaveContext.sohStats.dungeonKeys[i]);
|
||||
});
|
||||
SaveManager::Instance->SaveData("playTimer", gSaveContext.sohStats.playTimer);
|
||||
SaveManager::Instance->SaveData("pauseTimer", gSaveContext.sohStats.pauseTimer);
|
||||
SaveManager::Instance->SaveArray("timestamps", ARRAY_COUNT(gSaveContext.sohStats.timestamp), [](size_t i) {
|
||||
SaveManager::Instance->SaveData("", gSaveContext.sohStats.timestamp[i]);
|
||||
});
|
||||
SaveManager::Instance->SaveArray("counts", ARRAY_COUNT(gSaveContext.sohStats.count), [](size_t i) {
|
||||
SaveManager::Instance->SaveData("", gSaveContext.sohStats.count[i]);
|
||||
});
|
||||
});
|
||||
SaveManager::Instance->SaveArray("sceneFlags", ARRAY_COUNT(gSaveContext.sceneFlags), [](size_t i) {
|
||||
SaveManager::Instance->SaveStruct("", [&i]() {
|
||||
|
@ -56,6 +56,10 @@ void KaleidoScopeCall_Update(PlayState* play) {
|
||||
KaleidoMgrOverlay* kaleidoScopeOvl = &gKaleidoMgrOverlayTable[KALEIDO_OVL_KALEIDO_SCOPE];
|
||||
PauseContext* pauseCtx = &play->pauseCtx;
|
||||
|
||||
if (!gSaveContext.sohStats.gameComplete) {
|
||||
gSaveContext.sohStats.pauseTimer++;
|
||||
}
|
||||
|
||||
if ((pauseCtx->state != 0) || (pauseCtx->debugState != 0)) {
|
||||
if (pauseCtx->state == 1) {
|
||||
if (ShrinkWindow_GetCurrentVal() == 0) {
|
||||
@ -65,6 +69,7 @@ void KaleidoScopeCall_Update(PlayState* play) {
|
||||
pauseCtx->unk_1E4 = 0;
|
||||
pauseCtx->unk_1EC = 0;
|
||||
pauseCtx->state = (pauseCtx->state & 0xFFFF) + 1;
|
||||
gSaveContext.sohStats.count[COUNT_PAUSES]++;
|
||||
}
|
||||
} else if (pauseCtx->state == 8) {
|
||||
HREG(80) = 7;
|
||||
|
@ -1614,6 +1614,72 @@ void func_80084BF4(PlayState* play, u16 flag) {
|
||||
}
|
||||
}
|
||||
|
||||
// Gameplay stat tracking: Update time the item was acquired
|
||||
// (special cases for some duplicate items)
|
||||
void GameplayStats_SetTimestamp(u8 item) {
|
||||
|
||||
if (gSaveContext.sohStats.timestamp[item] != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
u32 time = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
|
||||
// Have items in Link's pocket shown as being obtained at 0.1 seconds
|
||||
if (time == 0) {
|
||||
time = 1;
|
||||
}
|
||||
|
||||
// Count any bottled item as a bottle
|
||||
if (item >= ITEM_BOTTLE && item <= ITEM_POE) {
|
||||
if (gSaveContext.sohStats.timestamp[ITEM_BOTTLE] == 0) {
|
||||
gSaveContext.sohStats.timestamp[ITEM_BOTTLE] = time;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Count any bombchu pack as bombchus
|
||||
if (item == ITEM_BOMBCHU || (item >= ITEM_BOMBCHUS_5 && item <= ITEM_BOMBCHUS_20)) {
|
||||
if (gSaveContext.sohStats.timestamp[ITEM_BOMBCHU] == 0) {
|
||||
gSaveContext.sohStats.timestamp[ITEM_BOMBCHU] = time;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
gSaveContext.sohStats.timestamp[item] = time;
|
||||
}
|
||||
|
||||
// Gameplay stat tracking: Update time the item was acquired
|
||||
// (special cases for rando items)
|
||||
void Randomizer_GameplayStats_SetTimestamp(uint16_t item) {
|
||||
|
||||
u32 time = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
|
||||
// Have items in Link's pocket shown as being obtained at 0.1 seconds
|
||||
if (time == 0) {
|
||||
time = 1;
|
||||
}
|
||||
|
||||
// Count any bottled item as a bottle
|
||||
if (item >= RG_EMPTY_BOTTLE && item <= RG_BOTTLE_WITH_BIG_POE) {
|
||||
if (gSaveContext.sohStats.timestamp[ITEM_BOTTLE] == 0) {
|
||||
gSaveContext.sohStats.timestamp[ITEM_BOTTLE] = time;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Count any bombchu pack as bombchus
|
||||
if (item >= RG_BOMBCHU_5 && item <= RG_BOMBCHU_DROP) {
|
||||
if (gSaveContext.sohStats.timestamp[ITEM_BOMBCHU] = 0) {
|
||||
gSaveContext.sohStats.timestamp[ITEM_BOMBCHU] = time;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (item == RG_MAGIC_SINGLE) {
|
||||
gSaveContext.sohStats.timestamp[ITEM_SINGLE_MAGIC] = time;
|
||||
}
|
||||
if (item == RG_DOUBLE_DEFENSE) {
|
||||
gSaveContext.sohStats.timestamp[ITEM_DOUBLE_DEFENSE] = time;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds the given item to Link's inventory.
|
||||
*
|
||||
@ -1631,6 +1697,9 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
s16 slot;
|
||||
s16 temp;
|
||||
|
||||
// Gameplay stats: Update the time the item was obtained
|
||||
GameplayStats_SetTimestamp(item);
|
||||
|
||||
slot = SLOT(item);
|
||||
if (item >= ITEM_STICKS_5) {
|
||||
slot = SLOT(sExtraItemBases[item - ITEM_STICKS_5]);
|
||||
@ -2291,6 +2360,9 @@ u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
|
||||
uint16_t i;
|
||||
uint16_t slot;
|
||||
|
||||
// Gameplay stats: Update the time the item was obtained
|
||||
Randomizer_GameplayStats_SetTimestamp(item);
|
||||
|
||||
slot = SLOT(item);
|
||||
if (item == RG_MAGIC_SINGLE) {
|
||||
gSaveContext.magicAcquired = true;
|
||||
@ -2975,6 +3047,10 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) {
|
||||
osSyncPrintf("***** 増減=%d (now=%d, max=%d) ***", healthChange, gSaveContext.health,
|
||||
gSaveContext.healthCapacity);
|
||||
|
||||
if (healthChange < 0) {
|
||||
gSaveContext.sohStats.count[COUNT_DAMAGE_TAKEN] += -healthChange;
|
||||
}
|
||||
|
||||
// If one-hit ko mode is on, any damage kills you and you cannot gain health.
|
||||
if (chaosEffectOneHitKO) {
|
||||
if (healthChange < 0) {
|
||||
@ -3041,6 +3117,43 @@ void Health_RemoveHearts(s16 hearts) {
|
||||
|
||||
void Rupees_ChangeBy(s16 rupeeChange) {
|
||||
gSaveContext.rupeeAccumulator += rupeeChange;
|
||||
|
||||
if (rupeeChange > 0) {
|
||||
gSaveContext.sohStats.count[COUNT_RUPEES_COLLECTED] += rupeeChange;
|
||||
}
|
||||
if (rupeeChange < 0) {
|
||||
gSaveContext.sohStats.count[COUNT_RUPEES_SPENT] += -rupeeChange;
|
||||
}
|
||||
}
|
||||
|
||||
void GameplayStats_UpdateAmmoUsed(s16 item, s16 ammoUsed) {
|
||||
|
||||
switch (item) {
|
||||
case ITEM_STICK:
|
||||
gSaveContext.sohStats.count[COUNT_AMMO_USED_STICK] += ammoUsed;
|
||||
break;
|
||||
case ITEM_NUT:
|
||||
gSaveContext.sohStats.count[COUNT_AMMO_USED_NUT] += ammoUsed;
|
||||
break;
|
||||
case ITEM_BOMB:
|
||||
gSaveContext.sohStats.count[COUNT_AMMO_USED_BOMB] += ammoUsed;
|
||||
break;
|
||||
case ITEM_BOW:
|
||||
gSaveContext.sohStats.count[COUNT_AMMO_USED_ARROW] += ammoUsed;
|
||||
break;
|
||||
case ITEM_SLINGSHOT:
|
||||
gSaveContext.sohStats.count[COUNT_AMMO_USED_SEED] += ammoUsed;
|
||||
break;
|
||||
case ITEM_BOMBCHU:
|
||||
gSaveContext.sohStats.count[COUNT_AMMO_USED_BOMBCHU] += ammoUsed;
|
||||
break;
|
||||
case ITEM_BEAN:
|
||||
gSaveContext.sohStats.count[COUNT_AMMO_USED_BEAN] += ammoUsed;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Inventory_ChangeAmmo(s16 item, s16 ammoChange) {
|
||||
@ -3100,6 +3213,10 @@ void Inventory_ChangeAmmo(s16 item, s16 ammoChange) {
|
||||
}
|
||||
|
||||
osSyncPrintf("合計 = (%d)\n", AMMO(item)); // "Total = (%d)"
|
||||
|
||||
if (ammoChange < 0) {
|
||||
GameplayStats_UpdateAmmoUsed(item, -ammoChange);
|
||||
}
|
||||
}
|
||||
|
||||
void Magic_Fill(PlayState* play) {
|
||||
|
@ -699,6 +699,24 @@ void Play_Update(PlayState* play) {
|
||||
play->transitionMode = 1;
|
||||
}
|
||||
|
||||
// Gameplay stats: Count button presses
|
||||
if (!gSaveContext.sohStats.gameComplete) {
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_A)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_A]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_B)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_B]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_CUP)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CUP]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_CRIGHT)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CLEFT]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_CLEFT)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CDOWN]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_CDOWN)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CRIGHT]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_DUP)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DUP]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_DRIGHT)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DRIGHT]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_DDOWN)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DDOWN]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_DLEFT)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DLEFT]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_L)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_L]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_R)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_R]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_Z)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_Z]++;}
|
||||
if (CHECK_BTN_ALL(input[0].press.button, BTN_START)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_START]++;}
|
||||
}
|
||||
|
||||
if (gTrnsnUnkState != 0) {
|
||||
switch (gTrnsnUnkState) {
|
||||
case 2:
|
||||
@ -1066,6 +1084,10 @@ void Play_Update(PlayState* play) {
|
||||
}
|
||||
|
||||
play->gameplayFrames++;
|
||||
// Gameplay stat tracking
|
||||
if (!gSaveContext.sohStats.gameComplete) {
|
||||
gSaveContext.sohStats.playTimer++;
|
||||
}
|
||||
|
||||
func_800AA178(1);
|
||||
|
||||
|
@ -1330,6 +1330,7 @@ void BossDodongo_DeathCutscene(BossDodongo* this, PlayState* play) {
|
||||
this->cameraAt.x = camera->at.x;
|
||||
this->cameraAt.y = camera->at.y;
|
||||
this->cameraAt.z = camera->at.z;
|
||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_KING_DODONGO] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
break;
|
||||
case 5:
|
||||
tempSin = Math_SinS(this->actor.shape.rot.y - 0x1388) * 150.0f;
|
||||
|
@ -893,6 +893,7 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) {
|
||||
Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF);
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_DEAD);
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_VOLVAGIA] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
} else if (damage) {
|
||||
BossFd2_SetupDamaged(this, play);
|
||||
this->work[FD2_DAMAGE_FLASH_TIMER] = 10;
|
||||
|
@ -2754,6 +2754,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) {
|
||||
func_80078914(&sZeroVec, NA_SE_EN_LAST_DAMAGE);
|
||||
Audio_QueueSeqCmd(0x100100FF);
|
||||
this->screenFlashTimer = 4;
|
||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_GANONDORF] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
} else {
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DAMAGE2);
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CUTBODY);
|
||||
|
@ -1670,6 +1670,8 @@ void func_8090120C(BossGanon2* this, PlayState* play) {
|
||||
if ((ABS(temp_a0_2) < 0x2000) && (sqrtf(SQ(temp_f14) + SQ(temp_f12)) < 70.0f) &&
|
||||
(player->swordState != 0) && (player->heldItemActionParam == PLAYER_AP_SWORD_MASTER)) {
|
||||
func_80064520(play, &play->csCtx);
|
||||
gSaveContext.sohStats.gameComplete = true;
|
||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
this->unk_39E = Play_CreateSubCamera(play);
|
||||
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT);
|
||||
Play_ChangeCameraStatus(play, this->unk_39E, CAM_STAT_ACTIVE);
|
||||
|
@ -1280,6 +1280,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) {
|
||||
if ((s8)this->actor.colChkInfo.health <= 0) {
|
||||
BossGanondrof_SetupDeath(this, play);
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_PHANTOM_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1832,6 +1832,7 @@ void BossGoma_UpdateHit(BossGoma* this, PlayState* play) {
|
||||
} else {
|
||||
BossGoma_SetupDefeated(this, play);
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_GOHMA] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
}
|
||||
|
||||
this->invincibilityFrames = 10;
|
||||
|
@ -1780,6 +1780,7 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) {
|
||||
if (((sMorphaTent1->csCamera == 0) && (sMorphaTent2 == NULL)) ||
|
||||
((sMorphaTent1->csCamera == 0) && (sMorphaTent2 != NULL) && (sMorphaTent2->csCamera == 0))) {
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_MORPHA] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF);
|
||||
this->csState = MO_DEATH_START;
|
||||
sMorphaTent1->drawActor = false;
|
||||
|
@ -2548,6 +2548,7 @@ void BossSst_HeadCollisionCheck(BossSst* this, PlayState* play) {
|
||||
if (Actor_ApplyDamage(&this->actor) == 0) {
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
BossSst_HeadSetupDeath(this, play);
|
||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_BONGO_BONGO] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
} else {
|
||||
BossSst_HeadSetupDamage(this);
|
||||
}
|
||||
|
@ -5285,6 +5285,7 @@ void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) {
|
||||
BossTw_TwinrovaSetupDeathCS(this, play);
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD);
|
||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_TWINROVA] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1394,6 +1394,7 @@ void BossVa_BodyPhase4(BossVa* this, PlayState* play) {
|
||||
if (sFightPhase >= PHASE_DEATH) {
|
||||
BossVa_SetupBodyDeath(this, play);
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_BARINADE] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
return;
|
||||
}
|
||||
this->actor.speedXZ = -10.0f;
|
||||
|
@ -268,6 +268,7 @@ void DoorKiller_Die(DoorKiller* this, PlayState* play) {
|
||||
Flags_SetSwitch(play, switchFlag);
|
||||
}
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DOOR_TRAP]++;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -883,6 +883,8 @@ void EnAm_Update(Actor* thisx, PlayState* play) {
|
||||
dustPosScale += 60.0f;
|
||||
}
|
||||
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ARMOS]++;
|
||||
|
||||
Actor_Kill(&this->dyna.actor);
|
||||
return;
|
||||
}
|
||||
|
@ -312,6 +312,7 @@ void EnAnubice_SetupDie(EnAnubice* this, PlayState* play) {
|
||||
}
|
||||
|
||||
this->actionFunc = EnAnubice_Die;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ANUBIS]++;
|
||||
}
|
||||
|
||||
void EnAnubice_Die(EnAnubice* this, PlayState* play) {
|
||||
|
@ -455,6 +455,7 @@ void EnBa_Update(Actor* thisx, PlayState* play) {
|
||||
this->actor.colChkInfo.health--;
|
||||
if (this->actor.colChkInfo.health == 0) {
|
||||
func_809B75A0(this, play);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_PARASITIC_TENTACLE]++;
|
||||
} else {
|
||||
func_809B7174(this);
|
||||
}
|
||||
|
@ -462,6 +462,19 @@ void EnBb_SetupDeath(EnBb* this, PlayState* play) {
|
||||
}
|
||||
this->action = BB_KILL;
|
||||
EnBb_SetupAction(this, EnBb_Death);
|
||||
|
||||
if (this->actor.params == ENBB_GREEN || this->actor.params == ENBB_GREEN_BIG) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_GREEN]++;
|
||||
}
|
||||
if (this->actor.params == ENBB_BLUE) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_BLUE]++;
|
||||
}
|
||||
if (this->actor.params == ENBB_WHITE) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_WHITE]++;
|
||||
}
|
||||
if (this->actor.params == ENBB_RED) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUBBLE_RED]++;
|
||||
}
|
||||
}
|
||||
|
||||
void EnBb_Death(EnBb* this, PlayState* play) {
|
||||
|
@ -646,6 +646,7 @@ void func_809BE26C(EnBigokuta* this, PlayState* play) {
|
||||
SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 50, NA_SE_EN_OCTAROCK_BUBLE);
|
||||
Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0xB0);
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BIG_OCTO]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -231,6 +231,7 @@ void EnBili_SetupDie(EnBili* this) {
|
||||
this->actor.flags &= ~ACTOR_FLAG_0;
|
||||
this->actionFunc = EnBili_Die;
|
||||
this->actor.speedXZ = 0.0f;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BIRI]++;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -532,6 +532,7 @@ void EnBox_Open(EnBox* this, PlayState* play) {
|
||||
|
||||
if (Animation_OnFrame(&this->skelanime, 30.0f)) {
|
||||
sfxId = NA_SE_EV_TBOX_UNLOCK;
|
||||
gSaveContext.sohStats.count[COUNT_CHESTS_OPENED]++;
|
||||
} else if (Animation_OnFrame(&this->skelanime, 90.0f)) {
|
||||
sfxId = NA_SE_EV_TBOX_OPEN;
|
||||
}
|
||||
|
@ -372,6 +372,7 @@ void EnBubble_Pop(EnBubble* this, PlayState* play) {
|
||||
if (EnBubble_Explosion(this, play) >= 0) {
|
||||
SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 60, NA_SE_EN_AWA_BREAK);
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SHABOM]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -575,6 +575,7 @@ void func_809D00F4(EnBw* this) {
|
||||
this->actor.speedXZ = 0.0f;
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLEWALK_DEAD);
|
||||
EnBw_SetupAction(this, func_809D014C);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_TORCH_SLUG]++;
|
||||
}
|
||||
|
||||
void func_809D014C(EnBw* this, PlayState* play) {
|
||||
|
@ -189,6 +189,7 @@ void EnCrow_SetupDamaged(EnCrow* this, PlayState* play) {
|
||||
void EnCrow_SetupDie(EnCrow* this) {
|
||||
this->actor.colorFilterTimer = 0;
|
||||
this->actionFunc = EnCrow_Die;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GUAY]++;
|
||||
}
|
||||
|
||||
void EnCrow_SetupTurnAway(EnCrow* this) {
|
||||
|
@ -406,6 +406,12 @@ void EnDekubaba_SetupPrunedSomersault(EnDekubaba* this) {
|
||||
this->actor.speedXZ = this->size * 3.0f;
|
||||
this->actor.flags |= ACTOR_FLAG_4 | ACTOR_FLAG_5;
|
||||
this->actionFunc = EnDekubaba_PrunedSomersault;
|
||||
|
||||
if (this->actor.params == DEKUBABA_BIG) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEKU_BABA_BIG]++;
|
||||
} else {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEKU_BABA]++;
|
||||
}
|
||||
}
|
||||
|
||||
void EnDekubaba_SetupShrinkDie(EnDekubaba* this) {
|
||||
@ -413,6 +419,12 @@ void EnDekubaba_SetupShrinkDie(EnDekubaba* this) {
|
||||
0.0f, ANIMMODE_ONCE, -3.0f);
|
||||
this->collider.base.acFlags &= ~AC_ON;
|
||||
this->actionFunc = EnDekubaba_ShrinkDie;
|
||||
|
||||
if (this->actor.params == DEKUBABA_BIG) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEKU_BABA_BIG]++;
|
||||
} else {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEKU_BABA]++;
|
||||
}
|
||||
}
|
||||
|
||||
void EnDekubaba_SetupStunnedVertical(EnDekubaba* this) {
|
||||
|
@ -233,6 +233,7 @@ void EnDekunuts_SetupDie(EnDekunuts* this) {
|
||||
this->actionFunc = EnDekunuts_Die;
|
||||
this->actor.speedXZ = 0.0f;
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DEAD);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_MAD_SCRUB]++;
|
||||
}
|
||||
|
||||
void EnDekunuts_Wait(EnDekunuts* this, PlayState* play) {
|
||||
|
@ -440,6 +440,7 @@ void EnDh_SetupDeath(EnDh* this) {
|
||||
this->actor.params = ENDH_DEATH;
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_DEAD);
|
||||
EnDh_SetupAction(this, EnDh_Death);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEAD_HAND]++;
|
||||
}
|
||||
|
||||
void EnDh_Death(EnDh* this, PlayState* play) {
|
||||
|
@ -504,6 +504,7 @@ void EnDns_Burrow(EnDns* this, PlayState* play) {
|
||||
}
|
||||
}
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BUSINESS_SCRUB]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,6 +183,7 @@ void func_809F6A20(EnDodojr* this) {
|
||||
this->unk_1FC = 3;
|
||||
this->actor.velocity.y = 10.0f;
|
||||
}
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DODONGO_BABY]++;
|
||||
}
|
||||
|
||||
void func_809F6AC4(EnDodojr* this) {
|
||||
|
@ -666,6 +666,7 @@ void EnDodongo_SetupDeath(EnDodongo* this, PlayState* play) {
|
||||
this->actor.flags &= ~ACTOR_FLAG_0;
|
||||
this->actor.speedXZ = 0.0f;
|
||||
EnDodongo_SetupAction(this, EnDodongo_Death);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DODONGO]++;
|
||||
}
|
||||
|
||||
void EnDodongo_Death(EnDodongo* this, PlayState* play) {
|
||||
|
@ -611,6 +611,7 @@ void EnEiyer_UpdateDamage(EnEiyer* this, PlayState* play) {
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_EIER_DEAD);
|
||||
this->actor.flags &= ~ACTOR_FLAG_0;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STINGER]++;
|
||||
}
|
||||
|
||||
// If underground, one hit kill
|
||||
|
@ -646,6 +646,7 @@ void EnFd_WaitForCore(EnFd* this, PlayState* play) {
|
||||
} else if (this->actor.params & FLG_COREDEAD) {
|
||||
this->actor.params = 0;
|
||||
this->spinTimer = 30;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLARE_DANCER]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,6 +223,15 @@ void EnFirefly_SetupDie(EnFirefly* this) {
|
||||
this->timer = 15;
|
||||
this->actor.speedXZ = 0.0f;
|
||||
this->actionFunc = EnFirefly_Die;
|
||||
if (this->actor.params == KEESE_NORMAL_FLY || this->actor.params == KEESE_NORMAL_PERCH) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_KEESE]++;
|
||||
}
|
||||
if (this->actor.params == KEESE_FIRE_FLY || this->actor.params == KEESE_FIRE_PERCH) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_KEESE_FIRE]++;
|
||||
}
|
||||
if (this->actor.params == KEESE_ICE_FLY) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_KEESE_ICE]++;
|
||||
}
|
||||
}
|
||||
|
||||
void EnFirefly_SetupRebound(EnFirefly* this) {
|
||||
|
@ -439,6 +439,7 @@ void EnFloormas_Die(EnFloormas* this, PlayState* play) {
|
||||
// Die
|
||||
Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x90);
|
||||
EnFloormas_SetupSmShrink(this, play);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLOORMASTER]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -363,6 +363,7 @@ void EnFz_ApplyDamage(EnFz* this, PlayState* play) {
|
||||
vec.z = this->actor.world.pos.z;
|
||||
EnFz_Damaged(this, play, &vec, 30, 10.0f);
|
||||
EnFz_SetupDespawn(this, play);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FREEZARD]++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1318,6 +1318,7 @@ void EnGeldB_SetupDefeated(EnGeldB* this) {
|
||||
this->actor.flags &= ~ACTOR_FLAG_0;
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_DEAD);
|
||||
EnGeldB_SetupAction(this, EnGeldB_Defeated);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GERUDO_THIEF]++;
|
||||
}
|
||||
|
||||
void EnGeldB_Defeated(EnGeldB* this, PlayState* play) {
|
||||
|
@ -396,6 +396,7 @@ void EnGoma_SetupDead(EnGoma* this) {
|
||||
Animation_GetLastFrame(&gObjectGolDeadTwitchingAnim), ANIMMODE_LOOP, -2.0f);
|
||||
this->actionFunc = EnGoma_Dead;
|
||||
this->actionTimer = 3;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GOHMA_LARVA]++;
|
||||
}
|
||||
|
||||
void EnGoma_Dead(EnGoma* this, PlayState* play) {
|
||||
@ -666,6 +667,7 @@ void EnGoma_UpdateHit(EnGoma* this, PlayState* play) {
|
||||
|
||||
EnGoma_SpawnHatchDebris(this, play);
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GOHMA_LARVA]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -428,6 +428,7 @@ void EnHintnuts_Leave(EnHintnuts* this, PlayState* play) {
|
||||
Actor_ChangeCategory(play, &play->actorCtx, this->actor.child, ACTORCAT_PROP);
|
||||
}
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DEKU_SCRUB]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -628,6 +628,7 @@ void func_80A7598C(EnIk* this) {
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_DEAD);
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_CUTBODY);
|
||||
EnIk_SetupAction(this, func_80A75A38);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE]++;
|
||||
}
|
||||
|
||||
void func_80A75A38(EnIk* this, PlayState* play) {
|
||||
@ -1446,6 +1447,7 @@ void func_80A781CC(Actor* thisx, PlayState* play) {
|
||||
}
|
||||
gSaveContext.eventChkInf[3] |= 0x1000;
|
||||
func_80A7735C(this, play);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_IRON_KNUCKLE_NABOORU]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,6 +180,7 @@ void EnKarebaba_SetupDying(EnKarebaba* this) {
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_JR_DEAD);
|
||||
this->actor.flags |= ACTOR_FLAG_4 | ACTOR_FLAG_5;
|
||||
this->actionFunc = EnKarebaba_Dying;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WITHERED_DEKU_BABA]++;
|
||||
}
|
||||
|
||||
void EnKarebaba_SetupDeadItemDrop(EnKarebaba* this, PlayState* play) {
|
||||
|
@ -310,6 +310,7 @@ void EnKusa_Main(EnKusa* this, PlayState* play) {
|
||||
EnKusa_SpawnFragments(this, play);
|
||||
EnKusa_DropCollectible(this, play);
|
||||
SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 20, NA_SE_EV_PLANT_BROKEN);
|
||||
gSaveContext.sohStats.count[COUNT_BUSHES_CUT]++;
|
||||
|
||||
if ((this->actor.params >> 4) & 1) {
|
||||
EnKusa_SpawnBugs(this, play);
|
||||
@ -378,6 +379,7 @@ void EnKusa_Fall(EnKusa* this, PlayState* play) {
|
||||
if (this->actor.bgCheckFlags & 0xB) {
|
||||
if (!(this->actor.bgCheckFlags & 0x20)) {
|
||||
SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 20, NA_SE_EV_PLANT_BROKEN);
|
||||
gSaveContext.sohStats.count[COUNT_BUSHES_CUT]++;
|
||||
}
|
||||
EnKusa_SpawnFragments(this, play);
|
||||
EnKusa_DropCollectible(this, play);
|
||||
|
@ -1400,12 +1400,14 @@ void EnMb_CheckColliding(EnMb* this, PlayState* play) {
|
||||
if (this->actor.params == ENMB_TYPE_CLUB) {
|
||||
if (this->actor.colChkInfo.health == 0) {
|
||||
EnMb_SetupClubDead(this);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_MOBLIN_CLUB]++;
|
||||
} else if (this->state != ENMB_STATE_CLUB_KNEELING) {
|
||||
EnMb_SetupClubDamaged(this);
|
||||
}
|
||||
} else {
|
||||
if (this->actor.colChkInfo.health == 0) {
|
||||
EnMb_SetupSpearDead(this);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_MOBLIN]++;
|
||||
} else {
|
||||
EnMb_SetupSpearDamaged(this);
|
||||
}
|
||||
|
@ -447,6 +447,7 @@ void EnNy_SetupDie(EnNy* this, PlayState* play) {
|
||||
}
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_NYU_DEAD);
|
||||
this->actionFunc = EnNy_Die;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SPIKE]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,6 +253,7 @@ void EnOkuta_SetupDie(EnOkuta* this) {
|
||||
Animation_MorphToPlayOnce(&this->skelAnime, &gOctorokDieAnim, -3.0f);
|
||||
this->timer = 0;
|
||||
this->actionFunc = EnOkuta_Die;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_OCTOROK]++;
|
||||
}
|
||||
|
||||
void EnOkuta_SetupFreeze(EnOkuta* this) {
|
||||
|
@ -590,6 +590,7 @@ void EnPeehat_Larva_StateSeekPlayer(EnPeehat* this, PlayState* play) {
|
||||
}
|
||||
Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x20);
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_PEAHAT_LARVA]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -759,6 +760,7 @@ void EnPeehat_StateAttackRecoil(EnPeehat* this, PlayState* play) {
|
||||
1);
|
||||
}
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_PEAHAT_LARVA]++;
|
||||
} else {
|
||||
EnPeehat_Ground_SetStateSeekPlayer(this);
|
||||
// Is PEAHAT_TYPE_GROUNDED
|
||||
@ -875,6 +877,7 @@ void EnPeehat_StateExplode(EnPeehat* this, PlayState* play) {
|
||||
Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x40);
|
||||
Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x40);
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_PEAHAT]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -577,6 +577,11 @@ void EnPoField_Death(EnPoField* this, PlayState* play) {
|
||||
255, 0, 0, 255, 1, 9, 1);
|
||||
if (this->actionTimer == 1) {
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_EXTINCT);
|
||||
if (this->actor.params == EN_PO_FIELD_BIG) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE_BIG]++;
|
||||
} else {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE]++;
|
||||
}
|
||||
}
|
||||
} else if (this->actionTimer == 28) {
|
||||
EnPoField_SetupSoulIdle(this, play);
|
||||
|
@ -1179,6 +1179,7 @@ void func_80ADC10C(EnPoSisters* this, PlayState* play) {
|
||||
} else {
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_SISTER_DEAD);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE_SISTERS]++;
|
||||
}
|
||||
func_80AD95D8(this);
|
||||
}
|
||||
|
@ -636,6 +636,11 @@ void func_80ADF15C(EnPoh* this, PlayState* play) {
|
||||
0, 0, 255, 1, 9, 1);
|
||||
if (this->unk_198 == 1) {
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_EXTINCT);
|
||||
if (this->actor.params == EN_POH_FLAT || this->actor.params == EN_POH_SHARP) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE_COMPOSER]++;
|
||||
} else {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_POE]++;
|
||||
}
|
||||
}
|
||||
} else if (this->unk_198 == 28) {
|
||||
EnPoh_SetupDeath(this, play);
|
||||
|
@ -639,6 +639,11 @@ void func_80AE3C20(EnRd* this) {
|
||||
this->actor.speedXZ = 0.0f;
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_REDEAD_DEAD);
|
||||
EnRd_SetupAction(this, func_80AE3C98);
|
||||
if (this->actor.params >= -1) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_REDEAD]++;
|
||||
} else {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_GIBDO]++;
|
||||
}
|
||||
}
|
||||
|
||||
void func_80AE3C98(EnRd* this, PlayState* play) {
|
||||
|
@ -439,6 +439,7 @@ void func_80AE5A9C(EnReeba* this, PlayState* play) {
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIVA_DEAD);
|
||||
Enemy_StartFinishingBlow(play, &this->actor);
|
||||
this->actionfunc = func_80AE5C38;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LEEVER]++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -493,6 +494,11 @@ void func_80AE5C38(EnReeba* this, PlayState* play) {
|
||||
}
|
||||
|
||||
Actor_Kill(&this->actor);
|
||||
if (this->isBig) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LEEVER_BIG]++;
|
||||
} else {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LEEVER]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -381,6 +381,7 @@ void EnRr_SetupDeath(EnRr* this) {
|
||||
this->actionFunc = EnRr_Death;
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_LIKE_DEAD);
|
||||
this->actor.flags &= ~ACTOR_FLAG_0;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LIKE_LIKE]++;
|
||||
}
|
||||
|
||||
void EnRr_SetupStunned(EnRr* this) {
|
||||
|
@ -456,6 +456,7 @@ void EnSb_Update(Actor* thisx, PlayState* play) {
|
||||
} else {
|
||||
Item_DropCollectible(play, &this->actor.world.pos, 8);
|
||||
}
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SHELLBLADE]++;
|
||||
Actor_Kill(&this->actor);
|
||||
}
|
||||
} else {
|
||||
|
@ -412,6 +412,7 @@ void func_80AFD7B4(EnSkb* this, PlayState* play) {
|
||||
this->unk_283 |= 4;
|
||||
EffectSsDeadSound_SpawnStationary(play, &this->actor.projectedPos, NA_SE_EN_STALKID_DEAD, 1, 1, 0x28);
|
||||
EnSkb_SetupAction(this, func_80AFD880);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STALCHILD]++;
|
||||
}
|
||||
|
||||
void func_80AFD880(EnSkb* this, PlayState* play) {
|
||||
|
@ -733,6 +733,7 @@ void EnSkj_SariasSongKidIdle(EnSkj* this, PlayState* play) {
|
||||
void EnSkj_SetupDie(EnSkj* this) {
|
||||
EnSkj_ChangeAnim(this, SKJ_ANIM_DIE);
|
||||
EnSkj_SetupAction(this, SKJ_ACTION_WAIT_FOR_DEATH_ANIM);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULL_KID]++;
|
||||
}
|
||||
|
||||
void EnSkj_WaitForDeathAnim(EnSkj* this, PlayState* play) {
|
||||
|
@ -465,6 +465,11 @@ s32 EnSt_CheckHitBackside(EnSt* this, PlayState* play) {
|
||||
this->deathTimer = 20;
|
||||
this->actor.gravity = -1.0f;
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALWALL_DEAD);
|
||||
if (this->actor.params == 1) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULLTULA_BIG]++;
|
||||
} else {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULLTULA]++;
|
||||
}
|
||||
|
||||
if (flags & 0x1F820) {
|
||||
// arrow, fire arrow, ice arrow, light arrow,
|
||||
|
@ -346,6 +346,7 @@ s32 func_80B0C9F0(EnSw* this, PlayState* play) {
|
||||
this->unk_38A = 1;
|
||||
this->unk_420 *= 4.0f;
|
||||
this->actionFunc = func_80B0D878;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULLTULA_GOLD]++;
|
||||
} else {
|
||||
this->actor.shape.shadowDraw = ActorShadow_DrawCircle;
|
||||
this->actor.shape.shadowAlpha = 0xFF;
|
||||
@ -354,6 +355,7 @@ s32 func_80B0C9F0(EnSw* this, PlayState* play) {
|
||||
this->actor.gravity = -1.0f;
|
||||
this->actor.flags &= ~ACTOR_FLAG_0;
|
||||
this->actionFunc = func_80B0DB00;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_SKULLWALLTULA]++;
|
||||
}
|
||||
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALWALL_DEAD);
|
||||
|
@ -1525,6 +1525,7 @@ void func_80862E6C(EnTest* this, PlayState* play) {
|
||||
}
|
||||
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STALFOS]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1633,6 +1634,7 @@ void func_808633E8(EnTest* this, PlayState* play) {
|
||||
}
|
||||
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STALFOS]++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1723,6 +1725,7 @@ void EnTest_Update(Actor* thisx, PlayState* play) {
|
||||
if ((floorProperty == 5) || (floorProperty == 0xC) ||
|
||||
func_80041D4C(&play->colCtx, this->actor.floorPoly, this->actor.floorBgId) == 9) {
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STALFOS]++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -764,8 +764,10 @@ void EnTite_FallApart(EnTite* this, PlayState* play) {
|
||||
if (BodyBreak_SpawnParts(&this->actor, &this->bodyBreak, play, this->actor.params + 0xB)) {
|
||||
if (this->actor.params == TEKTITE_BLUE) {
|
||||
Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0xE0);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_TEKTITE_BLUE]++;
|
||||
} else {
|
||||
Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0x40);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_TEKTITE_RED]++;
|
||||
}
|
||||
Actor_Kill(&this->actor);
|
||||
}
|
||||
|
@ -546,6 +546,7 @@ void EnTorch2_Update(Actor* thisx, PlayState* play2) {
|
||||
case ENTORCH2_DEATH:
|
||||
if (sAlpha - 13 <= 0) {
|
||||
sAlpha = 0;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DARK_LINK]++;
|
||||
Actor_Kill(&this->actor);
|
||||
return;
|
||||
}
|
||||
|
@ -289,6 +289,9 @@ void EnTp_SetupDie(EnTp* this) {
|
||||
}
|
||||
this->actionIndex = TAILPASARAN_ACTION_DIE;
|
||||
EnTp_SetupAction(this, EnTp_Die);
|
||||
if (this->actor.params == TAILPASARAN_HEAD) { // Only count the head, otherwise each body segment will increment
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_TAILPASARAN]++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -176,6 +176,7 @@ void EnTuboTrap_HandleImpact(EnTuboTrap* this, PlayState* play) {
|
||||
SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 40, NA_SE_EV_BOMB_DROP_WATER);
|
||||
EnTuboTrap_DropCollectible(this, play);
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLYING_POT]++;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -186,6 +187,7 @@ void EnTuboTrap_HandleImpact(EnTuboTrap* this, PlayState* play) {
|
||||
SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 40, NA_SE_EV_POT_BROKEN);
|
||||
EnTuboTrap_DropCollectible(this, play);
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLYING_POT]++;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -196,6 +198,7 @@ void EnTuboTrap_HandleImpact(EnTuboTrap* this, PlayState* play) {
|
||||
SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 40, NA_SE_EV_POT_BROKEN);
|
||||
EnTuboTrap_DropCollectible(this, play);
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLYING_POT]++;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -207,6 +210,7 @@ void EnTuboTrap_HandleImpact(EnTuboTrap* this, PlayState* play) {
|
||||
SoundSource_PlaySfxAtFixedWorldPos(play, &player2->actor.world.pos, 40, NA_SE_PL_BODY_HIT);
|
||||
EnTuboTrap_DropCollectible(this, play);
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLYING_POT]++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -216,6 +220,7 @@ void EnTuboTrap_HandleImpact(EnTuboTrap* this, PlayState* play) {
|
||||
SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 40, NA_SE_EV_POT_BROKEN);
|
||||
EnTuboTrap_DropCollectible(this, play);
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLYING_POT]++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -252,6 +252,7 @@ void EnVali_SetupDivideAndDie(EnVali* this, PlayState* play) {
|
||||
this->actor.flags &= ~ACTOR_FLAG_0;
|
||||
this->actor.draw = NULL;
|
||||
this->actionFunc = EnVali_DivideAndDie;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BARI]++;
|
||||
}
|
||||
|
||||
void EnVali_SetupStunned(EnVali* this) {
|
||||
|
@ -366,6 +366,7 @@ void EnVm_SetupDie(EnVm* this) {
|
||||
this->actor.speedXZ = Rand_ZeroOne() + 1.0f;
|
||||
this->actor.world.rot.y = Rand_CenteredFloat(65535.0f);
|
||||
EnVm_SetupAction(this, EnVm_Die);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_BEAMOS]++;
|
||||
}
|
||||
|
||||
void EnVm_Die(EnVm* this, PlayState* play) {
|
||||
|
@ -252,6 +252,7 @@ void EnWallmas_SetupDie(EnWallmas* this, PlayState* play) {
|
||||
|
||||
Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0xC0);
|
||||
this->actionFunc = EnWallmas_Die;
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WALLMASTER]++;
|
||||
}
|
||||
|
||||
void EnWallmas_SetupTakePlayer(EnWallmas* this, PlayState* play) {
|
||||
|
@ -574,6 +574,7 @@ void func_80B3368C(EnWeiyer* this, PlayState* play) {
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_EIER_DEAD);
|
||||
this->actor.flags &= ~ACTOR_FLAG_0;
|
||||
func_80B32724(this);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_STINGER]++;
|
||||
} else {
|
||||
func_80B325A0(this);
|
||||
}
|
||||
|
@ -1193,6 +1193,11 @@ void EnWf_SetupDie(EnWf* this) {
|
||||
this->actionTimer = this->skelAnime.animLength;
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_DEAD);
|
||||
EnWf_SetupAction(this, EnWf_Die);
|
||||
if (this->actor.params == WOLFOS_WHITE) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WOLFOS_WHITE]++;
|
||||
} else {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WOLFOS]++;
|
||||
}
|
||||
}
|
||||
|
||||
void EnWf_Die(EnWf* this, PlayState* play) {
|
||||
|
@ -111,6 +111,7 @@ void EnYukabyun_Break(EnYukabyun* this, PlayState* play) {
|
||||
EffectSsHahen_SpawnBurst(play, &this->actor.world.pos, 8.0f, 0, 1300, 300, 15, OBJECT_YUKABYUN, 10,
|
||||
gFloorTileEnemyFragmentDL);
|
||||
Actor_Kill(&this->actor);
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_FLOOR_TILE]++;
|
||||
}
|
||||
|
||||
void EnYukabyun_Update(Actor* thisx, PlayState* play) {
|
||||
|
@ -1921,6 +1921,12 @@ void EnZf_SetupDie(EnZf* this) {
|
||||
D_80B4A1B0 = 0;
|
||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DEAD);
|
||||
EnZf_SetupAction(this, EnZf_Die);
|
||||
|
||||
if (this->actor.params == ENZF_TYPE_DINOLFOS) {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_DINOLFOS]++;
|
||||
} else {
|
||||
gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_LIZALFOS]++;
|
||||
}
|
||||
}
|
||||
|
||||
void EnZf_Die(EnZf* this, PlayState* play) {
|
||||
|
@ -188,6 +188,7 @@ void ObjTsubo_AirBreak(ObjTsubo* this, PlayState* play) {
|
||||
sObjectIds[(this->actor.params >> 8) & 1], D_80BA1B8C[(this->actor.params >> 8) & 1]);
|
||||
}
|
||||
func_80033480(play, &this->actor.world.pos, 30.0f, 4, 20, 50, 1);
|
||||
gSaveContext.sohStats.count[COUNT_POTS_BROKEN]++;
|
||||
}
|
||||
|
||||
void ObjTsubo_WaterBreak(ObjTsubo* this, PlayState* play) {
|
||||
@ -216,6 +217,7 @@ void ObjTsubo_WaterBreak(ObjTsubo* this, PlayState* play) {
|
||||
(Rand_ZeroOne() * 95.0f) + 15.0f, 0, 32, 70, KAKERA_COLOR_NONE,
|
||||
sObjectIds[(this->actor.params >> 8) & 1], D_80BA1B8C[(this->actor.params >> 8) & 1]);
|
||||
}
|
||||
gSaveContext.sohStats.count[COUNT_POTS_BROKEN]++;
|
||||
}
|
||||
|
||||
void ObjTsubo_SetupWaitForObject(ObjTsubo* this) {
|
||||
|
@ -1460,6 +1460,12 @@ void func_808327F8(Player* this, f32 arg1) {
|
||||
}
|
||||
|
||||
func_800F4010(&this->actor.projectedPos, sfxId, arg1);
|
||||
// Gameplay stats: Count footsteps
|
||||
// Only count while game isn't complete and don't count Link's idle animations or crawling in crawlspaces
|
||||
if (!gSaveContext.sohStats.gameComplete && !(this->stateFlags2 & PLAYER_STATE2_28) &&
|
||||
!(this->stateFlags2 & PLAYER_STATE2_18)) {
|
||||
gSaveContext.sohStats.count[COUNT_STEPS]++;
|
||||
}
|
||||
}
|
||||
|
||||
void func_80832854(Player* this) {
|
||||
@ -1950,6 +1956,10 @@ void func_80833A20(Player* this, s32 newSwordState) {
|
||||
if ((this->swordAnimation < 0x10) || (this->swordAnimation >= 0x14)) {
|
||||
func_80832698(this, voiceSfx);
|
||||
}
|
||||
|
||||
if (this->heldItemActionParam >= PLAYER_AP_SWORD_MASTER && this->heldItemActionParam <= PLAYER_AP_SWORD_BGS) {
|
||||
gSaveContext.sohStats.count[COUNT_SWORD_SWINGS]++;
|
||||
}
|
||||
}
|
||||
|
||||
this->swordState = newSwordState;
|
||||
@ -5285,6 +5295,7 @@ void func_8083BC04(Player* this, PlayState* play) {
|
||||
func_80835C58(play, this, func_80844708, 0);
|
||||
LinkAnimation_PlayOnceSetSpeed(play, &this->skelAnime, D_80853914[PLAYER_ANIMGROUP_16][this->modelAnimType],
|
||||
1.25f * D_808535E8);
|
||||
gSaveContext.sohStats.count[COUNT_ROLLS]++;
|
||||
}
|
||||
|
||||
s32 func_8083BC7C(Player* this, PlayState* play) {
|
||||
@ -6258,6 +6269,8 @@ s32 func_8083E5A8(Player* this, PlayState* play) {
|
||||
func_80837C0C(play, this, 3, 0.0f, 0.0f, 0, 20);
|
||||
this->getItemId = GI_NONE;
|
||||
this->getItemEntry = (GetItemEntry) GET_ITEM_NONE;
|
||||
// Gameplay stats: Increment Ice Trap count
|
||||
gSaveContext.sohStats.count[COUNT_ICE_TRAPS]++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -8648,6 +8661,7 @@ void func_80844708(Player* this, PlayState* play) {
|
||||
func_8002F7DC(&this->actor, NA_SE_PL_BODY_HIT);
|
||||
func_80832698(this, NA_SE_VO_LI_CLIMB_END);
|
||||
this->unk_850 = 1;
|
||||
gSaveContext.sohStats.count[COUNT_BONKS]++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -12887,6 +12901,8 @@ void func_8084E6D4(Player* this, PlayState* play) {
|
||||
func_80837C0C(play, this, 3, 0.0f, 0.0f, 0, 20);
|
||||
this->getItemId = GI_NONE;
|
||||
this->getItemEntry = (GetItemEntry)GET_ITEM_NONE;
|
||||
// Gameplay stats: Increment Ice Trap count
|
||||
gSaveContext.sohStats.count[COUNT_ICE_TRAPS]++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user