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:
Sarge-117 2022-11-22 17:04:40 -08:00 committed by GitHub
parent 9cfe7bff47
commit 9c162fc0ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
78 changed files with 950 additions and 0 deletions

View file

@ -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;

View file

@ -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) {

View file

@ -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);