#include #include "constants.h" #include "game/atan2f.h" #include "game/bot.h" #include "game/challenge.h" #include "game/chrai.h" #include "game/chraicommands.h" #include "game/debug.h" #include "game/lang.h" #include "game/mplayer/mplayer.h" #include "game/mplayer/scenarios.h" #include "game/mplayer/setup.h" #include "game/pad.h" #include "game/playermgr.h" #include "game/prop.h" #include "game/training.h" #include "bss.h" #include "lib/dma.h" #include "lib/rng.h" #include "data.h" #include "types.h" u8 g_MpFeaturesForceUnlocked[40]; u8 g_MpFeaturesUnlocked[80]; u32 g_MpChallengeIndex = 0; struct mpconfigfull *g_MpCurrentChallengeConfig = NULL; struct challenge g_MpChallenges[] = { { L_OPTIONS_406, MPCONFIG_CHALLENGE01 }, // "Challenge 1" { L_OPTIONS_407, MPCONFIG_CHALLENGE02 }, // "Challenge 2" { L_OPTIONS_408, MPCONFIG_CHALLENGE03 }, // "Challenge 3" { L_OPTIONS_409, MPCONFIG_CHALLENGE04 }, // "Challenge 4" { L_OPTIONS_410, MPCONFIG_CHALLENGE05 }, // "Challenge 5" { L_OPTIONS_411, MPCONFIG_CHALLENGE06 }, // "Challenge 6" { L_OPTIONS_412, MPCONFIG_CHALLENGE07 }, // "Challenge 7" { L_OPTIONS_413, MPCONFIG_CHALLENGE08 }, // "Challenge 8" { L_OPTIONS_414, MPCONFIG_CHALLENGE09 }, // "Challenge 9" { L_OPTIONS_415, MPCONFIG_CHALLENGE10 }, // "Challenge 10" { L_OPTIONS_416, MPCONFIG_CHALLENGE11 }, // "Challenge 11" { L_OPTIONS_417, MPCONFIG_CHALLENGE12 }, // "Challenge 12" { L_OPTIONS_418, MPCONFIG_CHALLENGE13 }, // "Challenge 13" { L_OPTIONS_419, MPCONFIG_CHALLENGE14 }, // "Challenge 14" { L_OPTIONS_420, MPCONFIG_CHALLENGE15 }, // "Challenge 15" { L_OPTIONS_421, MPCONFIG_CHALLENGE16 }, // "Challenge 16" { L_OPTIONS_422, MPCONFIG_CHALLENGE17 }, // "Challenge 17" { L_OPTIONS_423, MPCONFIG_CHALLENGE18 }, // "Challenge 18" { L_OPTIONS_424, MPCONFIG_CHALLENGE19 }, // "Challenge 19" { L_OPTIONS_425, MPCONFIG_CHALLENGE20 }, // "Challenge 20" { L_OPTIONS_426, MPCONFIG_CHALLENGE21 }, // "Challenge 21" { L_OPTIONS_427, MPCONFIG_CHALLENGE22 }, // "Challenge 22" { L_OPTIONS_428, MPCONFIG_CHALLENGE23 }, // "Challenge 23" { L_OPTIONS_429, MPCONFIG_CHALLENGE24 }, // "Challenge 24" { L_OPTIONS_430, MPCONFIG_CHALLENGE25 }, // "Challenge 25" { L_OPTIONS_431, MPCONFIG_CHALLENGE26 }, // "Challenge 26" { L_OPTIONS_432, MPCONFIG_CHALLENGE27 }, // "Challenge 27" { L_OPTIONS_433, MPCONFIG_CHALLENGE28 }, // "Challenge 28" { L_OPTIONS_434, MPCONFIG_CHALLENGE29 }, // "Challenge 29" { L_OPTIONS_435, MPCONFIG_CHALLENGE30 }, // "Challenge 30" }; bool challengeIsAvailable(s32 challengeindex) { return (g_MpChallenges[challengeindex].availability & 1) != 0; } bool challengeIsAvailableToPlayer(s32 chrnum, s32 challengeindex) { if ((g_MpSetup.chrslots & (1 << chrnum)) == 0) { return 0; } return ((g_MpChallenges[challengeindex].availability & (2 << chrnum)) != 0); } bool challengeIsAvailableToAnyPlayer(s32 challengeindex) { return (g_MpChallenges[challengeindex].availability & (((g_MpSetup.chrslots & 0xf) << 1) | 1)) != 0; } void challengeDetermineUnlockedFeatures(void) { s32 challengeindex; s32 numgifted; // number of unlocked but not completed challenges u8 flag; s32 prev; s32 i; s32 j; s32 k; // Clear all challenge availability for (challengeindex = 0; challengeindex < ARRAYCOUNT(g_MpChallenges); challengeindex++) { g_MpChallenges[challengeindex].availability = 0; } numgifted = 0; // Mark challenges completed by any player for (challengeindex = 0; challengeindex < ARRAYCOUNT(g_MpChallenges); challengeindex++) { flag = 0; if (challengeIsCompletedByAnyPlayerWithNumPlayers(challengeindex, 1) || challengeIsCompletedByAnyPlayerWithNumPlayers(challengeindex, 2) || challengeIsCompletedByAnyPlayerWithNumPlayers(challengeindex, 3) || challengeIsCompletedByAnyPlayerWithNumPlayers(challengeindex, 4)) { // Completed challenge flag = 1; } else if (challengeindex < 4) { // Not yet completed, but challenges 1-4 are always available flag = 1; numgifted++; } else if (challengeindex > 0 && (challengeIsCompletedByAnyPlayerWithNumPlayers(challengeindex - 1, 1) || challengeIsCompletedByAnyPlayerWithNumPlayers(challengeindex - 1, 2) || challengeIsCompletedByAnyPlayerWithNumPlayers(challengeindex - 1, 3) || challengeIsCompletedByAnyPlayerWithNumPlayers(challengeindex - 1, 4))) { // Challenges are available if their previous one is complete flag = 1; numgifted++; } #ifdef DEBUG else if (debugIsAllChallengesEnabled()) { flag = 1; } #endif g_MpChallenges[challengeindex].availability |= flag; } // Gift up to 4 challenges for (challengeindex = 0; numgifted < 4 && challengeindex < ARRAYCOUNT(g_MpChallenges); challengeindex++) { if ((g_MpChallenges[challengeindex].availability & 1) == 0) { g_MpChallenges[challengeindex].availability |= 1; numgifted++; } } // Now same as above, but per player for (j = 0; j < MAX_PLAYERS; j++) { numgifted = 0; for (challengeindex = 0; challengeindex < ARRAYCOUNT(g_MpChallenges); challengeindex++) { flag = 0; if (challengeIsCompletedByPlayerWithNumPlayers(j, challengeindex, 1) || challengeIsCompletedByPlayerWithNumPlayers(j, challengeindex, 2) || challengeIsCompletedByPlayerWithNumPlayers(j, challengeindex, 3) || challengeIsCompletedByPlayerWithNumPlayers(j, challengeindex, 4)) { // Completed challenge flag = 2 << j; } else if (challengeindex < 4) { // Not yet completed, but challenges 1-4 are always available flag = 2 << j; numgifted++; } else if (challengeindex > 0) { // Challenges are available if their previous one is complete prev = challengeindex - 1; if (challengeIsCompletedByPlayerWithNumPlayers(j, prev, 1) || challengeIsCompletedByPlayerWithNumPlayers(j, prev, 2) || challengeIsCompletedByPlayerWithNumPlayers(j, prev, 3) || challengeIsCompletedByPlayerWithNumPlayers(j, prev, 4)) { flag = 2 << j; numgifted++; } } g_MpChallenges[challengeindex].availability |= flag; } // Gift up to 4 challenges for (challengeindex = 0; numgifted < 4 && challengeindex < ARRAYCOUNT(g_MpChallenges); challengeindex++) { if ((g_MpChallenges[challengeindex].availability & (2 << j)) == 0) { g_MpChallenges[challengeindex].availability |= 2 << j; numgifted++; } } } for (j = 0; j < ARRAYCOUNT(g_MpFeaturesUnlocked); j++) { flag = 0; for (challengeindex = 0; challengeindex < ARRAYCOUNT(g_MpChallenges); challengeindex++) { if (challengeIsAvailableToAnyPlayer(challengeindex)) { for (i = 0; i < ARRAYCOUNT(g_MpChallenges[challengeindex].unlockfeatures); i++) { if (g_MpChallenges[challengeindex].unlockfeatures[i] == j) { flag |= 1; } } } } for (i = 0; i < ARRAYCOUNT(g_MpFeaturesForceUnlocked); i++) { if (g_MpFeaturesForceUnlocked[i] == j) { flag |= 1; } } for (challengeindex = 0; challengeindex < ARRAYCOUNT(g_MpChallenges); challengeindex++) { for (prev = 0; prev < MAX_PLAYERS; prev++) { if (challengeIsAvailableToPlayer(prev, challengeindex)) { for (i = 0; i < ARRAYCOUNT(g_MpChallenges[challengeindex].unlockfeatures); i++) { if (g_MpChallenges[challengeindex].unlockfeatures[i] == j) { flag |= 2 << prev; } } } } } g_MpFeaturesUnlocked[j] = flag; } for (j = 0; j < func0f188bcc(); j++) { struct mpweapon *weapon = &g_MpWeapons[j]; if (weapon->unlockfeature > 0 && func0f19cbcc(weapon->weaponnum)) { g_MpFeaturesUnlocked[weapon->unlockfeature] |= 1; } } func0f1895e8(); // If the ability to have 8 simulants hasn't been unlocked, limit them to 4 if (!challengeIsFeatureUnlocked(MPFEATURE_8BOTS)) { for (k = 4; k < MAX_BOTS; k++) { if (g_MpSetup.chrslots & (1 << (MAX_PLAYERS + k))) { mpRemoveSimulant(k); } } if (g_Vars.mpquickteamnumsims > 4) { g_Vars.mpquickteamnumsims = 4; } } } void challengePerformSanityChecks(void) { if (g_BossFile.locktype == MPLOCKTYPE_CHALLENGE) { s32 numplayers = 0; s32 i; // Reset player handicaps for (i = 0; i < MAX_PLAYERS; i++) { if (g_MpSetup.chrslots & (1 << i)) { g_PlayerConfigsArray[i].handicap = 0x80; numplayers++; } } // Turn off all simulants and turn them on if enabled // for this number of players g_MpSetup.chrslots &= 0x000f; for (i = 0; i < MAX_BOTS; i++) { g_BotConfigsArray[i].difficulty = g_MpSimulantDifficultiesPerNumPlayers[i][numplayers - 1]; if (g_BotConfigsArray[i].difficulty != BOTDIFF_DISABLED) { g_MpSetup.chrslots |= 1 << (i + MAX_PLAYERS); } } if (g_MpSetup.scenario == MPSCENARIO_KINGOFTHEHILL) { g_Vars.mphilltime = 10; } } else if (!challengeIsFeatureUnlocked(MPFEATURE_8BOTS)) { // Limit to 4 players and 4 simulants g_MpSetup.chrslots &= 0x00ff; } } s32 challengeGetNumAvailable(void) { s32 challengeindex; s32 count = 0; for (challengeindex = 0; challengeindex < ARRAYCOUNT(g_MpChallenges); challengeindex++) { if (challengeIsAvailableToAnyPlayer(challengeindex)) { count++; } } return count; } char *challengeGetName(s32 challengeindex) { return langGet(g_MpChallenges[challengeindex].name); } char *challengeGetNameBySlot(s32 slot) { s32 index = 0; s32 i; for (i = 0; i < ARRAYCOUNT(g_MpChallenges); i++) { if (challengeIsAvailableToAnyPlayer(i)) { if (index == slot) { return challengeGetName(i); } index++; } } return ""; } void challengeSetCurrentBySlot(s32 slotnum) { s32 challengeindex; g_MpChallengeIndex = 0; for (challengeindex = 0; challengeindex < ARRAYCOUNT(g_MpChallenges); challengeindex++) { if (challengeIsAvailableToAnyPlayer(challengeindex)) { if (slotnum == 0) { g_MpChallengeIndex = challengeindex; break; } slotnum--; } } challengeApply(); } s32 challengeGetCurrent(void) { return g_MpChallengeIndex; } bool challengeIsCompletedByAnyChrWithNumPlayersBySlot(s32 slot, s32 numplayers) { s32 availableindex = 0; s32 i; for (i = 0; i < ARRAYCOUNT(g_MpChallenges); i++) { if (challengeIsAvailableToAnyPlayer(i)) { if (availableindex == slot) { return challengeIsCompletedByAnyPlayerWithNumPlayers(i, numplayers); } availableindex++; } } return false; } bool challengeIsCompletedByChrWithNumPlayersBySlot(s32 mpchrnum, s32 slot, s32 numplayers) { s32 availableindex = 0; s32 i; for (i = 0; i < ARRAYCOUNT(g_MpChallenges); i++) { if (challengeIsAvailableToAnyPlayer(i)) { if (availableindex == slot) { return challengeIsCompletedByPlayerWithNumPlayers(mpchrnum, i, numplayers); } availableindex++; } } return false; } #ifdef PLATFORM_N64 #define BTYPE s32 #else #define BTYPE uintptr_t #endif struct mpconfigfull *challengeLoadConfig(s32 confignum, u8 *buffer, s32 len) { struct mpconfigfull *mpconfig; u8 buffer2[sizeof(struct mpstrings) + 40]; struct mpstrings *loadedstrings; BTYPE bank; u32 language_id = langGetFileNumOffset(); #ifdef PLATFORM_N64 extern struct mpconfig _mpconfigsSegmentRomStart[]; #else extern u8 EXT_SEG _mpconfigsSegmentRomStart; #endif extern struct mpstrings EXT_SEG _mpstringsESegmentRomStart; extern struct mpstrings EXT_SEG _mpstringsJSegmentRomStart; extern struct mpstrings EXT_SEG _mpstringsPSegmentRomStart; extern struct mpstrings EXT_SEG _mpstringsGSegmentRomStart; extern struct mpstrings EXT_SEG _mpstringsFSegmentRomStart; extern struct mpstrings EXT_SEG _mpstringsSSegmentRomStart; extern struct mpstrings EXT_SEG _mpstringsISegmentRomStart; extern struct mpstrings EXT_SEG _mpstringsESegmentRomEnd; extern struct mpstrings EXT_SEG _mpstringsJSegmentRomEnd; extern struct mpstrings EXT_SEG _mpstringsPSegmentRomEnd; extern struct mpstrings EXT_SEG _mpstringsGSegmentRomEnd; extern struct mpstrings EXT_SEG _mpstringsFSegmentRomEnd; extern struct mpstrings EXT_SEG _mpstringsSSegmentRomEnd; extern struct mpstrings EXT_SEG _mpstringsISegmentRomEnd; BTYPE banks[][2] = { { (BTYPE)REF_SEG _mpstringsESegmentRomStart, (BTYPE)REF_SEG _mpstringsESegmentRomEnd }, { (BTYPE)REF_SEG _mpstringsJSegmentRomStart, (BTYPE)REF_SEG _mpstringsJSegmentRomEnd }, { (BTYPE)REF_SEG _mpstringsPSegmentRomStart, (BTYPE)REF_SEG _mpstringsPSegmentRomEnd }, { (BTYPE)REF_SEG _mpstringsGSegmentRomStart, (BTYPE)REF_SEG _mpstringsGSegmentRomEnd }, { (BTYPE)REF_SEG _mpstringsFSegmentRomStart, (BTYPE)REF_SEG _mpstringsFSegmentRomEnd }, { (BTYPE)REF_SEG _mpstringsSSegmentRomStart, (BTYPE)REF_SEG _mpstringsSSegmentRomEnd }, { (BTYPE)REF_SEG _mpstringsISegmentRomStart, (BTYPE)REF_SEG _mpstringsISegmentRomEnd }, }; // Load mpconfigs #ifdef PLATFORM_N64 mpconfig = dmaExecWithAutoAlign(buffer, (BTYPE)&_mpconfigsSegmentRomStart[confignum], sizeof(struct mpconfig)); #else mpconfig = dmaExecWithAutoAlign(buffer, (BTYPE)REF_SEG _mpconfigsSegmentRomStart + confignum * sizeof(struct mpconfig), sizeof(struct mpconfig)); #endif // Load mpstrings bank = banks[language_id][0]; loadedstrings = dmaExecWithAutoAlign(buffer2, bank + confignum * sizeof(struct mpstrings), sizeof(struct mpstrings)); mpconfig->strings = *loadedstrings; return mpconfig; } struct mpconfigfull *challengeLoad(s32 challengeindex, u8 *buffer, s32 len) { return challengeLoadConfig(g_MpChallenges[challengeindex].confignum, buffer, len); } struct mpconfigfull *challengeLoadBySlot(s32 n, u8 *buffer, s32 len) { s32 numavailable = 0; s32 challengeindex; for (challengeindex = 0; challengeindex < ARRAYCOUNT(g_MpChallenges); challengeindex++) { if (challengeIsAvailableToAnyPlayer(challengeindex)) { if (numavailable == n) { return challengeLoad(challengeindex, buffer, len); } numavailable++; } } return 0; } struct mpconfigfull *challengeLoadCurrent(u8 *buffer, s32 len) { return challengeLoad(g_MpChallengeIndex, buffer, len); } /** * This is adding featurenum to the array, provided it's unique. */ s32 challengeForceUnlockFeature(s32 featurenum, u8 *array, s32 tail, s32 len) { s32 i; for (i = 0; i < tail; i++) { if (array[i] == featurenum) { break; } } if (i >= tail && tail < len) { array[tail] = featurenum; tail++; } return tail; } s32 challengeForceUnlockSetupFeatures(struct mpsetup *setup, u8 *array, s32 len) { s32 index = 0; s32 i; // Force unlock the weapons (if never held before) for (i = 0; i < ARRAYCOUNT(setup->weapons); i++) { s32 featurenum = g_MpWeapons[setup->weapons[i]].unlockfeature; if (featurenum) { index = challengeForceUnlockFeature(featurenum, array, index, len); } } // Force unlock the stage for (i = 0; i < mpGetNumStages(); i++) { if (g_MpArenas[i].stagenum == setup->stagenum) { s32 featurenum = g_MpArenas[i].requirefeature; if (featurenum) { index = challengeForceUnlockFeature(featurenum, array, index, len); } } } // Force unlock the scenario if (setup->scenario <= MPSCENARIO_CAPTURETHECASE) { s32 featurenum = g_MpScenarioOverviews[setup->scenario].requirefeature; if (featurenum) { index = challengeForceUnlockFeature(featurenum, array, index, len); } } // Force unlock the scenario options if (setup->options & MPOPTION_ONEHITKILLS) { index = challengeForceUnlockFeature(MPFEATURE_ONEHITKILLS, array, index, len); } if (setup->options & (MPOPTION_SLOWMOTION_ON | MPOPTION_SLOWMOTION_SMART)) { index = challengeForceUnlockFeature(MPFEATURE_SLOWMOTION, array, index, len); } return index; } void challengeForceUnlockConfigFeatures(struct mpconfig *config, u8 *array, s32 len, s32 challengeindex) { s32 index = challengeForceUnlockSetupFeatures(&config->setup, array, len); s32 featurenum; s32 numplayers; s32 i; for (i = 0; i < MAX_BOTS; i++) { s32 simtype = mpFindBotProfile(config->simulants[i].type, BOTDIFF_NORMAL); if (simtype >= 0) { featurenum = g_BotProfiles[simtype].requirefeature; if (featurenum) { index = challengeForceUnlockFeature(featurenum, array, index, len); } } for (numplayers = 0; numplayers < MAX_PLAYERS; numplayers++) { simtype = mpFindBotProfile(0, config->simulants[i].difficulties[numplayers]); if (simtype >= 0) { featurenum = g_BotProfiles[simtype].requirefeature; if (featurenum) { index = challengeForceUnlockFeature(featurenum, array, index, len); } } } if (config->simulants[i].mpbodynum < ARRAYCOUNT(g_MpBodies)) { featurenum = g_MpBodies[config->simulants[i].mpbodynum].requirefeature; if (featurenum) { index = challengeForceUnlockFeature(featurenum, array, index, len); } } if (config->simulants[i].mpheadnum < ARRAYCOUNT(g_MpHeads)) { featurenum = g_MpHeads[config->simulants[i].mpheadnum].requirefeature; if (featurenum) { index = challengeForceUnlockFeature(featurenum, array, index, len); } } } #if VERSION >= VERSION_NTSC_1_0 if (challengeindex >= 25) { index = challengeForceUnlockFeature(MPFEATURE_BOTDIFF_DARK, array, index, len); } else if (challengeindex >= 20) { index = challengeForceUnlockFeature(MPFEATURE_STAGE_CARPARK, array, index, len); } else if (challengeindex >= 15) { index = challengeForceUnlockFeature(MPFEATURE_SCENARIO_PAC, array, index, len); } if (challengeindex >= 10) { index = challengeForceUnlockFeature(MPFEATURE_8BOTS, array, index, len); } #else if (challengeindex >= 10) { index = challengeForceUnlockFeature(MPFEATURE_8BOTS, array, index, len); } if (challengeindex >= 15) { index = challengeForceUnlockFeature(MPFEATURE_SCENARIO_PAC, array, index, len); } if (challengeindex >= 20) { index = challengeForceUnlockFeature(MPFEATURE_STAGE_CARPARK, array, index, len); } #endif // Clear the remainder of the array for (i = index; i < len; i++) { array[i] = 0; } } void challengeForceUnlockBotFeatures(void) { s32 numsims = 0; s32 index = challengeForceUnlockSetupFeatures(&g_MpSetup, g_MpFeaturesForceUnlocked, ARRAYCOUNT(g_MpFeaturesForceUnlocked)); s32 i; for (i = 0; i < ARRAYCOUNT(g_BotConfigsArray); i++) { // Force unlock the simulant type s32 simtypeindex = mpFindBotProfile(g_BotConfigsArray[i].type, BOTDIFF_NORMAL); if (simtypeindex >= 0) { s32 featurenum = g_BotProfiles[simtypeindex].requirefeature; if (featurenum) { index = challengeForceUnlockFeature(featurenum, g_MpFeaturesForceUnlocked, index, ARRAYCOUNT(g_MpFeaturesForceUnlocked)); } } // Force unlock the simulant difficulty simtypeindex = mpFindBotProfile(BOTTYPE_GENERAL, g_BotConfigsArray[i].difficulty); if (simtypeindex >= 0) { s32 featurenum = g_BotProfiles[simtypeindex].requirefeature; if (featurenum) { index = challengeForceUnlockFeature(featurenum, g_MpFeaturesForceUnlocked, index, ARRAYCOUNT(g_MpFeaturesForceUnlocked)); } } if (simtypeindex >= 0) { numsims++; } // Force unlock the simulant's body if (g_BotConfigsArray[i].base.mpbodynum < ARRAYCOUNT(g_MpBodies)) { s32 featurenum = g_MpBodies[g_BotConfigsArray[i].base.mpbodynum].requirefeature; if (featurenum) { index = challengeForceUnlockFeature(featurenum, g_MpFeaturesForceUnlocked, index, ARRAYCOUNT(g_MpFeaturesForceUnlocked)); } } // Force unlock the simulant's head if (g_BotConfigsArray[i].base.mpheadnum < ARRAYCOUNT(g_MpHeads)) { s32 featurenum = g_MpHeads[g_BotConfigsArray[i].base.mpheadnum].requirefeature; if (featurenum) { index = challengeForceUnlockFeature(featurenum, g_MpFeaturesForceUnlocked, index, ARRAYCOUNT(g_MpFeaturesForceUnlocked)); } } } // Force unlock 8 simulants if (numsims > 4) { index = challengeForceUnlockFeature(MPFEATURE_8BOTS, g_MpFeaturesForceUnlocked, index, ARRAYCOUNT(g_MpFeaturesForceUnlocked)); } // Clear the remainder of the array for (i = index; i < ARRAYCOUNT(g_MpFeaturesForceUnlocked); i++) { g_MpFeaturesForceUnlocked[i] = 0; } challengeDetermineUnlockedFeatures(); } void challengeRemoveForceUnlocks(void) { s32 i; for (i = 0; i < ARRAYCOUNT(g_MpFeaturesForceUnlocked); i++) { g_MpFeaturesForceUnlocked[i] = 0; } challengeDetermineUnlockedFeatures(); } void challengeApply(void) { s32 i; u8 buffer[0x1ca]; mpApplyConfig(challengeLoadCurrent(buffer, 0x1ca)); mpSetLock(MPLOCKTYPE_CHALLENGE, 5); for (i = 0; i < MAX_PLAYERS; i++) { g_PlayerConfigsArray[i].base.team = 0; } } s32 challengeRemovePlayerLock(void) { return mpSetLock(MPLOCKTYPE_NONE, 0); } void challengeLoadAndStoreCurrent(u8 *buffer, s32 len) { g_MpCurrentChallengeConfig = challengeLoadCurrent(buffer, len); } void challengeUnsetCurrent(void) { g_MpCurrentChallengeConfig = NULL; } bool challengeIsLoaded(void) { return g_MpCurrentChallengeConfig != NULL; } char *challengeGetCurrentDescription(void) { if (g_MpCurrentChallengeConfig) { return g_MpCurrentChallengeConfig->strings.description; } return ""; } char *challengeGetConfigDescription(struct mpconfigfull *mpconfig) { if (mpconfig) { return mpconfig->strings.description; } return ""; } /** * Return the index of the first incomplete challenge, but if it's in the first * 4 then return index 4 because the first 4 are always shown. */ s32 challengeGetAutoFocusedIndex(s32 mpchrnum) { s32 challengeindex; s32 index = 0; for (challengeindex = ARRAYCOUNT(g_MpChallenges) - 1; challengeindex >= 0; challengeindex--) { if (challengeIsCompletedByPlayerWithNumPlayers(mpchrnum, challengeindex, 1) || challengeIsCompletedByPlayerWithNumPlayers(mpchrnum, challengeindex, 2) || challengeIsCompletedByPlayerWithNumPlayers(mpchrnum, challengeindex, 3) || challengeIsCompletedByPlayerWithNumPlayers(mpchrnum, challengeindex, 4)) { index = challengeindex + 1; break; } } if (index < 4) { index = 4; } return index; } char *challengeGetName2(s32 playernum, s32 challengeindex) { return langGet(g_MpChallenges[challengeindex].name); } bool challengeIsCompletedByPlayerWithNumPlayers2(s32 mpchrnum, s32 index, s32 numplayers) { return challengeIsCompletedByPlayerWithNumPlayers(mpchrnum, index, numplayers); } bool challengeIsCompletedByAnyPlayerWithNumPlayers(s32 index, s32 numplayers) { return (g_MpChallenges[index].completions[numplayers - 1] & 1) != 0; } void challengeSetCompletedByAnyPlayerWithNumPlayers(s32 index, s32 numplayers, bool completed) { if (completed) { g_MpChallenges[index].completions[numplayers - 1] |= 1; return; } g_MpChallenges[index].completions[numplayers - 1] &= ~1; } bool challengeIsCompletedByPlayerWithNumPlayers(s32 mpchrnum, s32 index, s32 numplayers) { return (g_MpChallenges[index].completions[numplayers - 1] & (2 << mpchrnum)) != 0; } void challengeSetCompletedByPlayerWithNumPlayers(u32 mpchrnum, s32 index, s32 numplayers, bool completed) { if (completed) { g_MpChallenges[index].completions[numplayers - 1] |= 2 << mpchrnum; return; } g_MpChallenges[index].completions[numplayers - 1] &= ~(2 << mpchrnum); } bool challengeIsCompleteForEndscreen(void) { s32 prevplayernum = g_Vars.currentplayernum; s32 result = false; s32 aborted = false; s32 i; u32 stack; for (i = 0; i < PLAYERCOUNT(); i++) { setCurrentPlayerNum(i); if (g_Vars.currentplayer->aborted) { aborted = true; } } setCurrentPlayerNum(prevplayernum); if (!aborted) { struct ranking rankings[MAX_MPCHRS]; mpGetTeamRankings(rankings); if (rankings[0].teamnum == 0) { result = true; } } return result; } void challengeConsiderMarkingComplete(void) { bool result = challengeIsCompleteForEndscreen(); #if VERSION >= VERSION_NTSC_1_0 && defined(DEBUG) if ((g_CheatsActiveBank0 == 0 && g_CheatsActiveBank1 == 0) && (result || debugIsSetCompleteEnabled())) #elif VERSION >= VERSION_NTSC_1_0 if (g_CheatsActiveBank0 == 0 && g_CheatsActiveBank1 == 0 && result) #else if (result && g_CheatsActiveBank0 == 0 && g_CheatsActiveBank1 == 0) #endif { u32 prevplayernum; s32 i; challengeSetCompletedByAnyPlayerWithNumPlayers(g_MpChallengeIndex, PLAYERCOUNT(), 1); prevplayernum = g_Vars.currentplayernum; for (i = 0; i < PLAYERCOUNT(); i++) { setCurrentPlayerNum(i); challengeSetCompletedByPlayerWithNumPlayers(g_Vars.currentplayerstats->mpindex, g_MpChallengeIndex, PLAYERCOUNT(), true); } setCurrentPlayerNum(prevplayernum); challengeDetermineUnlockedFeatures(); } } bool challengeIsFeatureUnlocked(s32 featurenum) { if (featurenum == 0) { return true; } return (g_MpFeaturesUnlocked[featurenum] & 1) != 0; } bool challengeIsFeatureUnlockedByPlayer(u32 numplayers, s32 featurenum) { if (featurenum == 0) { return true; } return (g_MpFeaturesUnlocked[featurenum] & (2 << numplayers)) != 0; } bool challengeIsFeatureUnlockedByDefault(s32 featurenum) { if (featurenum) { return false; } return true; }