From 2b967480e02fced52f40fbbdf4e02bc9caa5d926 Mon Sep 17 00:00:00 2001 From: abaresk <46002898+abaresk@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:09:28 -0400 Subject: [PATCH] Document Battle Arcade fitness score (#1225) --- .../frontier/battle_arcade/main.h | 2 +- include/overlay104/ov104_02237DD8.h | 2 +- include/overlay104/struct_battle_arcade.h | 2 +- .../frontier/battle_arcade/main.c | 37 +++--- src/overlay104/frscrcmd_battle_arcade.c | 4 +- src/overlay104/ov104_02237DD8.c | 121 +++++++++--------- 6 files changed, 86 insertions(+), 82 deletions(-) diff --git a/include/applications/frontier/battle_arcade/main.h b/include/applications/frontier/battle_arcade/main.h index 28a1b26db8..68e71b1444 100644 --- a/include/applications/frontier/battle_arcade/main.h +++ b/include/applications/frontier/battle_arcade/main.h @@ -9,7 +9,7 @@ typedef struct BattleArcadeAppArgs { SaveData *saveData; u8 challengeType; u8 padding_05[2]; - u8 unk_07; + u8 fitnessScore; int *unk_08; u8 *rouletteSpeed; u8 *selectedEffect; diff --git a/include/overlay104/ov104_02237DD8.h b/include/overlay104/ov104_02237DD8.h index 6d2b103f02..4ee81c4822 100644 --- a/include/overlay104/ov104_02237DD8.h +++ b/include/overlay104/ov104_02237DD8.h @@ -21,7 +21,7 @@ void ov104_022384A8(BattleArcade *param0); void ov104_022384B4(BattleArcade *param0); void ov104_022384D4(BattleArcade *param0); void ov104_022384DC(BattleArcade *param0); -int ov104_02238538(BattleArcade *param0, Party *param1, Party *param2, int param3); +int BattleArcade_FitnessScore(BattleArcade *battleArcade, Party *party1, Party *party2, int totalTurnsElapsed); void ov104_02238658(void *param0, FrontierGraphics *param1); void ov104_02238728(void *param0, FrontierGraphics *param1); void ov104_02238764(BattleArcade *param0, FrontierGraphics *param1, u16 param2); diff --git a/include/overlay104/struct_battle_arcade.h b/include/overlay104/struct_battle_arcade.h index 3d186677e7..bec1fadb82 100644 --- a/include/overlay104/struct_battle_arcade.h +++ b/include/overlay104/struct_battle_arcade.h @@ -23,7 +23,7 @@ typedef struct BattleArcade { u16 currentStreak; u16 unk_1A; u8 unk_1C; - u8 unk_1D; + u8 fitnessScore; u8 unk_1E; u8 unk_1F; u16 unk_20; diff --git a/src/applications/frontier/battle_arcade/main.c b/src/applications/frontier/battle_arcade/main.c index 37a65ebcd7..72076771e4 100644 --- a/src/applications/frontier/battle_arcade/main.c +++ b/src/applications/frontier/battle_arcade/main.c @@ -153,15 +153,17 @@ static const struct { { 0, 1, 0, 1, 0, 1, 0 }, // ARCADE_EFFECT_GET_3_BP }; +// Columns: Foe, Ally, Env, Bonus effect weights. +// Rows: categoryTier, derived from fitness static const u8 sCategoryWeights[][NUM_ARCADE_EFFECT_CATEGORIES] = { - { 15, 15, 40, 30 }, - { 35, 20, 30, 15 }, - { 30, 30, 35, 5 }, - { 25, 40, 30, 5 }, - { 10, 75, 10, 5 } + { 15, 15, 40, 30 }, // tier 0 + { 35, 20, 30, 15 }, // tier 1 + { 30, 30, 35, 5 }, // tier 2 + { 25, 40, 30, 5 }, // tier 3 + { 10, 75, 10, 5 }, // tier 4 }; -static const u8 Unk_ov108_0224367C[] = { +static const u8 sCategoryTierFitnessThresholds[] = { 21, 16, 10, @@ -180,8 +182,13 @@ typedef struct BattleArcadeApp { u8 cursorPosID; u8 numReceivedMsgs; u8 cursorUpdateTimer; - u8 categoryWeightTier; - u8 unk_12; + u8 categoryTier; + // How well you did in the previous battle, based on mons fainted, status conditions, and turns + // elapsed. + // + // Higher fitness scores are more likely to yield environmental and bonus effects. Lower + // fitness scores are more likely to yield ally effects. + u8 fitnessScore; u8 unusedFlag : 1; u8 pointlessTimer : 7; u8 resultCursorPos; @@ -303,7 +310,7 @@ BOOL BattleArcadeApp_Init(ApplicationManager *appMan, int *state) app->round = args->round; app->battleStreak = args->currentStreak; app->partnerBattleStreak = args->partnersStreak; - app->unk_12 = args->unk_07; + app->fitnessScore = args->fitnessScore; app->cursorPosPtr = &args->cursorPos; app->options = SaveData_GetOptions(app->saveData); app->party = args->party; @@ -1326,13 +1333,13 @@ static void GetAvailableEffects(BattleArcadeApp *app) static void GetEffectCategoryCounts(BattleArcadeApp *app) { int i; - for (i = 0; i < NELEMS(Unk_ov108_0224367C); i++) { - if (app->unk_12 >= Unk_ov108_0224367C[i]) { + for (i = 0; i < NELEMS(sCategoryTierFitnessThresholds); i++) { + if (app->fitnessScore >= sCategoryTierFitnessThresholds[i]) { break; } } - app->categoryWeightTier = i; + app->categoryTier = i; for (i = 0; i < app->numAvailableEffects; i++) { if (BattleArcade_GetCategoryFromEffect(app->availableEffects[i]) == ARCADE_EFFECT_CATEGORY_FOE) { @@ -1352,7 +1359,7 @@ static void AssignEffectsToGrid(BattleArcadeApp *app) u8 effectsIndex = 0; for (int i = 0; i < GRID_SIZE; i++) { - u8 category = GetRandomCategory(app, app->categoryWeightTier); + u8 category = GetRandomCategory(app, app->categoryTier); int numEffectsInCategory = app->availableEffectsPerCategory[category]; int startOffset = 0; @@ -1406,14 +1413,14 @@ static void AssignEffectsToGrid(BattleArcadeApp *app) } } -static u8 GetRandomCategory(BattleArcadeApp *app, u8 weightsIndex) +static u8 GetRandomCategory(BattleArcadeApp *app, u8 categoryTier) { u8 category; u8 sum = 0; u16 rand = LCRNG_Next() % 100; for (category = ARCADE_EFFECT_CATEGORY_FOE; category < NUM_ARCADE_EFFECT_CATEGORIES; category++) { - sum += sCategoryWeights[weightsIndex][category]; + sum += sCategoryWeights[categoryTier][category]; if (rand < sum) { break; diff --git a/src/overlay104/frscrcmd_battle_arcade.c b/src/overlay104/frscrcmd_battle_arcade.c index e93292c0db..05b72a4280 100644 --- a/src/overlay104/frscrcmd_battle_arcade.c +++ b/src/overlay104/frscrcmd_battle_arcade.c @@ -119,7 +119,7 @@ BOOL FrontierScrCmd_BattleArcade_CleanupBattle(FrontierScriptContext *ctx) BattleArcade *battleArcade = BattleFrontier_GetFacilityStruct(ctx->scriptMan->frontier); FieldBattleDTO *dto = battleArcade->dto; - battleArcade->unk_1D = ov104_02238538(battleArcade, dto->parties[0], dto->parties[2], dto->totalTurnsElapsed); + battleArcade->fitnessScore = BattleArcade_FitnessScore(battleArcade, dto->parties[0], dto->parties[2], dto->totalTurnsElapsed); battleArcade->wonBattle = CheckPlayerWonBattle(dto->resultMask); if (battleArcade->activeEffect == ARCADE_EFFECT_SWAP_MONS) { @@ -200,7 +200,7 @@ static void SetupBattleArcadeAppArgs(BattleArcadeAppArgs *args, BattleArcade *ba args->round = ov104_0223C124(battleArcade); args->unk_14 = &battleArcade->unk_20; args->selectedEffect = &battleArcade->activeEffect; - args->unk_07 = battleArcade->unk_1D; + args->fitnessScore = battleArcade->fitnessScore; args->currentStreak = battleArcade->currentStreak; args->partnersStreak = battleArcade->unk_A78; args->rouletteSpeed = &battleArcade->unk_1C; diff --git a/src/overlay104/ov104_02237DD8.c b/src/overlay104/ov104_02237DD8.c index 9df7ec0d27..29c35e2809 100644 --- a/src/overlay104/ov104_02237DD8.c +++ b/src/overlay104/ov104_02237DD8.c @@ -48,8 +48,8 @@ void ov104_02238210(BattleArcade *param0); static u16 ov104_02238264(BattleArcadeAppArgs *param0, u8 param1); void ov104_022384D4(BattleArcade *param0); void ov104_022384DC(BattleArcade *param0); -int ov104_02238538(BattleArcade *param0, Party *param1, Party *param2, int param3); -static int ov104_02238584(BattleArcade *param0, Party *param1, Party *param2, u8 param3); +int BattleArcade_FitnessScore(BattleArcade *battleArcade, Party *party1, Party *party2, int totalTurnsElapsed); +static int BattleArcade_BaseFitnessScore(BattleArcade *battleArcade, Party *party1, Party *party2, u8 partySize); void ov104_02238658(void *param0, FrontierGraphics *param1); void ov104_02238728(void *param0, FrontierGraphics *param1); void ov104_02238764(BattleArcade *param0, FrontierGraphics *param1, u16 param2); @@ -489,104 +489,101 @@ void ov104_022384DC(BattleArcade *param0) return; } -static const u8 Unk_ov104_0223FAF0[5] = { - 0x8, - 0x6, - 0x4, - 0x0, - 0x0 +static const u8 sStatusConditionFitness[5] = { + 8, + 6, + 4, + 0, + 0 }; -static const u8 Unk_ov104_0223FAE8[5] = { - 0x6, - 0x4, - 0x2, - 0x0, - 0x0 +static const u8 sFaintedMonsFitness[5] = { + 6, + 4, + 2, + 0, + 0 }; -static const u8 Unk_ov104_0223FAFE[5][2] = { - { 0x3, 0xA }, - { 0x5, 0x6 }, - { 0x7, 0x4 }, - { 0x9, 0x2 }, - { 0xA, 0x0 } +static const struct { + u8 turns; + u8 score; +} sTurnsElapsedFitness[5] = { + { 3, 10 }, + { 5, 6 }, + { 7, 4 }, + { 9, 2 }, + { 10, 0 } }; -int ov104_02238538(BattleArcade *param0, Party *param1, Party *param2, int param3) +int BattleArcade_FitnessScore(BattleArcade *battleArcade, Party *party1, Party *party2, int totalTurnsElapsed) { - u8 v0; - int v1; - Pokemon *v2; - int v3 = 0; - v0 = BattleArcade_GetPlayerPartySize(param0->challengeType, 0); + u8 partySize; + int i; + int fitness = 0; + partySize = BattleArcade_GetPlayerPartySize(battleArcade->challengeType, FALSE); - v3 += ov104_02238584(param0, param1, param2, v0); + fitness += BattleArcade_BaseFitnessScore(battleArcade, party1, party2, partySize); - for (v1 = 0; v1 < 5; v1++) { - if (param3 < Unk_ov104_0223FAFE[v1][0]) { - v3 += Unk_ov104_0223FAFE[v1][1]; + for (i = 0; i < 5; i++) { + if (totalTurnsElapsed < sTurnsElapsedFitness[i].turns) { + fitness += sTurnsElapsedFitness[i].score; break; } } - return v3; + return fitness; } -static int ov104_02238584(BattleArcade *param0, Party *param1, Party *param2, u8 param3) +static int BattleArcade_BaseFitnessScore(BattleArcade *battleArcade, Party *party1, Party *party2, u8 partySize) { - u32 v0; - u8 v1; - u8 v2; - int v3, v4; - Pokemon *v5; + u8 faintedMons; + u8 monsWithStatusCondition; + int i, fitness; + Pokemon *mon; - v4 = 0; - v1 = 0; - v2 = 0; + fitness = 0; + faintedMons = 0; + monsWithStatusCondition = 0; - for (v3 = 0; v3 < param3; v3++) { - v5 = Party_GetPokemonBySlotIndex(param1, v3); + for (i = 0; i < partySize; i++) { + mon = Party_GetPokemonBySlotIndex(party1, i); - if (Pokemon_GetValue(v5, MON_DATA_SPECIES_EXISTS, NULL) == 0) { + if (Pokemon_GetValue(mon, MON_DATA_SPECIES_EXISTS, NULL) == 0) { continue; } - v0 = Pokemon_GetValue(v5, MON_DATA_HP, NULL); - - if (v0 == 0) { - v1++; + if (Pokemon_GetValue(mon, MON_DATA_HP, NULL) == 0) { + faintedMons++; } - if (Pokemon_GetValue(v5, MON_DATA_STATUS, NULL) != 0) { - v2++; + if (Pokemon_GetValue(mon, MON_DATA_STATUS, NULL) != 0) { + monsWithStatusCondition++; } } - if (BattleArcade_IsMultiPlayerChallenge(param0->challengeType) == 1) { - for (v3 = 0; v3 < param3; v3++) { - v5 = Party_GetPokemonBySlotIndex(param2, v3); + if (BattleArcade_IsMultiPlayerChallenge(battleArcade->challengeType) == TRUE) { + for (i = 0; i < partySize; i++) { + mon = Party_GetPokemonBySlotIndex(party2, i); - if (Pokemon_GetValue(v5, MON_DATA_SPECIES_EXISTS, NULL) == 0) { + if (Pokemon_GetValue(mon, MON_DATA_SPECIES_EXISTS, NULL) == 0) { continue; } - v0 = Pokemon_GetValue(v5, MON_DATA_HP, NULL); - - if (v0 == 0) { - v1++; + if (Pokemon_GetValue(mon, MON_DATA_HP, NULL) == 0) { + faintedMons++; } - if (Pokemon_GetValue(v5, MON_DATA_STATUS, NULL) != 0) { - v2++; + if (Pokemon_GetValue(mon, MON_DATA_STATUS, NULL) != 0) { + monsWithStatusCondition++; } } } - v4 += Unk_ov104_0223FAF0[v2]; - v4 += Unk_ov104_0223FAE8[v1]; + fitness += sStatusConditionFitness[monsWithStatusCondition]; + fitness += sFaintedMonsFitness[faintedMons]; - return v4; + return fitness; } void ov104_02238658(void *param0, FrontierGraphics *param1)