#include "global.h" #include "gpu_regs.h" #include "bg.h" #include "malloc.h" #include "battle.h" #include "battle_anim.h" #include "contest.h" #include "contest_link.h" #include "data.h" #include "decompress.h" #include "graphics.h" #include "link.h" #include "m4a.h" #include "main.h" #include "menu.h" #include "overworld.h" #include "palette.h" #include "random.h" #include "new_game.h" #include "script.h" #include "sound.h" #include "sprite.h" #include "string_util.h" #include "task.h" #include "text.h" #include "tv.h" #include "scanline_effect.h" #include "util.h" #include "contest_util.h" #include "dma3.h" #include "battle_message.h" #include "event_scripts.h" #include "event_data.h" #include "strings.h" #include "contest_effect.h" #include "contest_link.h" #include "international_string_util.h" #include "data.h" #include "contest_ai.h" #include "constants/event_objects.h" #include "constants/items.h" #include "constants/moves.h" #include "constants/rgb.h" #include "constants/songs.h" // This file's functions. static void LoadContestPalettes(void); static void Task_StartContestWaitFade(u8 taskId); static void Task_TryStartLinkContest(u8 taskId); static void Task_CommunicateMonIdxs(u8 taskId); static void Task_EndCommunicateMonIdxs(u8 taskId); static void Task_ReadyStartLinkContest(u8 taskId); static bool8 SetupContestGraphics(u8 *stateVar); static void Task_WaitToRaiseCurtainAtStart(u8 taskId); static void Task_RaiseCurtainAtStart(u8 taskId); static void VBlankCB_Contest(void); static void CB2_ContestMain(void); static void Task_DisplayAppealNumberText(u8 taskId); static void Task_TryShowMoveSelectScreen(u8 taskId); static void Task_ShowMoveSelectScreen(u8 taskId); static void Task_HandleMoveSelectInput(u8 taskId); static void DrawMoveSelectArrow(s8); static void EraseMoveSelectArrow(s8); static void Task_SelectedMove(u8 taskId); static void Task_EndCommunicateMoveSelections(u8 taskId); static void Task_HideMoveSelectScreen(u8 taskId); static void Task_HideApplauseMeterForAppealStart(u8 taskId); static void Task_WaitHideApplauseMeterForAppealStart(u8 taskId); static void Task_AppealSetup(u8 taskId); static void Task_DoAppeals(u8 taskId); static void Task_EndWaitForLink(u8); static void SpriteCB_MonSlideIn(struct Sprite *); static void SpriteCB_MonSlideOut(struct Sprite *); static void Task_FinishRoundOfAppeals(u8); static void Task_ReadyUpdateHeartSliders(u8); static void Task_UpdateHeartSliders(u8); static void Task_WaitForHeartSliders(u8); static void Task_RestorePlttBufferUnfaded(u8); static void Task_WaitPrintRoundResult(u8); static void Task_PrintRoundResultText(u8); static void Task_ReUpdateHeartSliders(u8); static void Task_WaitForHeartSlidersAgain(u8); static void Task_DropCurtainAtRoundEnd(u8); static void Task_TryStartNextRoundOfAppeals(u8); static void Task_StartNewRoundOfAppeals(u8); static void Task_EndAppeals(u8); static void Task_WaitForOutOfTimeMsg(u8); static void Task_DropCurtainAtAppealsEnd(u8); static void Task_TryCommunicateFinalStandings(u8); static void Task_CommunicateFinalStandings(u8); static void Task_EndCommunicateFinalStandings(u8); static void Task_ContestReturnToField(u8); static void FieldCB_ContestReturnToField(void); static bool8 IsPlayerLinkLeader(void); static void PrintContestantTrainerName(u8); static void PrintContestantTrainerNameWithColor(u8 a0, u8 a1); static void PrintContestantMonName(u8); static void PrintContestantMonNameWithColor(u8, u8); static u8 CreateJudgeSprite(void); static u8 CreateJudgeSpeechBubbleSprite(void); static u8 CreateContestantSprite(u16, u32, u32, u32); static void PrintContestMoveDescription(u16); static u16 SanitizeSpecies(u16); static void ContestClearGeneralTextWindow(void); static u16 GetChosenMove(u8); static void GetAllChosenMoves(void); static void ContestPrintLinkStandby(void); static void FillContestantWindowBgs(void); static void CreateSliderHeartSprites(void); static void SetBottomSliderHeartsInvisibility(bool8); static void CreateNextTurnSprites(void); static void CreateApplauseMeterSprite(void); static void CreateJudgeAttentionEyeTask(void); static void CreateUnusedBlendTask(void); static void ContestDebugDoPrint(void); static void DrawContestantWindows(void); static void ApplyNextTurnOrder(void); static void SlideApplauseMeterIn(void); static void SlideApplauseMeterOut(void); static void SetBgForCurtainDrop(void); static void UpdateContestantBoxOrder(void); static void Task_StartDropCurtainAtRoundEnd(u8); static void AnimateSliderHearts(u8); static void CreateInvisibleBattleTargetSprite(void); static void Contest_StartTextPrinter(const u8 *, u32); static void ContestBG_FillBoxWithIncrementingTile(u8, u16, u8, u8, u8, u8, u8, s16); static bool32 Contest_RunTextPrinters(void); static void Contest_SetBgCopyFlags(u32 flagIndex); static void CalculateFinalScores(void); static void CalculateAppealMoveImpact(u8); static void SetMoveAnimAttackerData(u8); static void BlinkContestantBox(u8, u8); static u8 CreateContestantBoxBlinkSprites(u8); static u16 SanitizeMove(u16); static void SetMoveSpecificAnimData(u8); static void SetMoveTargetPosition(u16); static void ClearMoveAnimData(u8); static void StopFlashJudgeAttentionEye(u8); static void DrawUnnervedSymbols(void); static void PrintAppealMoveResultText(u8, u8); static void DoJudgeSpeechBubble(u8); static void ShowHideNextTurnGfx(bool8); static u8 UpdateAppealHearts(s16, s16, u8); static bool8 UpdateConditionStars(u8, u8); static bool8 DrawStatusSymbol(u8); static void DrawStatusSymbols(void); static void StartStopFlashJudgeAttentionEye(u8); static void BlendAudienceBackground(s8, s8); static void ShowAndUpdateApplauseMeter(s8 unused); static void AnimateAudience(void); static void UpdateApplauseMeter(void); static void RankContestants(void); static void SetAttentionLevels(void); static void UpdateHeartSliders(void); static bool8 SlidersDoneUpdating(void); static void ContestBG_FillBoxWithTile(u8, u16, u8, u8, u8, u8, u8); static void Contest_PrintTextToBg0WindowStd(u32, const u8 *); static s16 GetContestantRound2Points(u8); static void DetermineFinalStandings(void); static bool8 DidContestantPlaceHigher(s32, s32, struct ContestFinalStandings *); static void Task_UpdateAppealHearts(u8); static void SpriteCB_UpdateHeartSlider(struct Sprite *); static void Task_FlashJudgeAttentionEye(u8); static void Task_StopFlashJudgeAttentionEye(u8); static void Task_UnusedBlend(u8); static void InitUnusedBlendTaskData(u8); static void UpdateBlendTaskContestantData(u8); static void SpriteCB_BlinkContestantBox(struct Sprite *); static void SpriteCB_EndBlinkContestantBox(struct Sprite *sprite); static u8 StartApplauseOverflowAnimation(void); static void Task_ApplauseOverflowAnimation(u8); static void Task_SlideApplauseMeterIn(u8); static void Task_SlideApplauseMeterOut(u8); static void Task_ShowAndUpdateApplauseMeter(u8); static void Task_AnimateAudience(u8); static void Task_BlendAudienceBackground(u8); static const u8 *GetTurnOrderNumberGfx(u8); static void Task_UpdateCurtainDropAtRoundEnd(u8); static void Task_ResetForNextRound(u8); static void Task_WaitRaiseCurtainAtRoundEnd(u8); static void Task_StartRaiseCurtainAtRoundEnd(u8); static void Task_WaitForSliderHeartAnim(u8); static void SetBattleTargetSpritePosition(void); static void CalculateContestLiveUpdateData(void); static void SetConestLiveUpdateTVData(void); static void SetContestLiveUpdateFlags(u8); static void ContestDebugPrintBitStrings(void); static void StripPlayerNameForLinkContest(u8 *); static void StripMonNameForLinkContest(u8 *, s32); static void SwapMoveDescAndContestTilemaps(void); // An index into a palette where the text color for each contestant is stored. // Contestant 0 will use palette color 10, contestant 1 will use color 11, etc. #define CONTESTANT_TEXT_COLOR_START 10 enum { // The "{Pokemon Name} / {Trainer Name}" windows. WIN_CONTESTANT0_NAME, WIN_CONTESTANT1_NAME, WIN_CONTESTANT2_NAME, WIN_CONTESTANT3_NAME, WIN_GENERAL_TEXT, // The available moves, from top to bottom WIN_MOVE0, WIN_MOVE1, WIN_MOVE2, WIN_MOVE3, // The small "/" character between the move category and the // appeal/jam display WIN_SLASH, WIN_MOVE_DESCRIPTION }; enum { JUDGE_SYMBOL_SWIRL, JUDGE_SYMBOL_SWIRL_UNUSED, JUDGE_SYMBOL_ONE_EXCLAMATION, JUDGE_SYMBOL_TWO_EXCLAMATIONS, JUDGE_SYMBOL_NUMBER_ONE_UNUSED, JUDGE_SYMBOL_NUMBER_ONE, JUDGE_SYMBOL_NUMBER_FOUR, JUDGE_SYMBOL_QUESTION_MARK, JUDGE_SYMBOL_STAR, }; enum { STAT_SYMBOL_CIRCLE, STAT_SYMBOL_WAVE, STAT_SYMBOL_X, STAT_SYMBOL_SWIRL, STAT_SYMBOL_SQUARE, }; enum { CONTEST_DEBUG_MODE_OFF, CONTEST_DEBUG_MODE_PRINT_POINT_TOTAL, CONTEST_DEBUG_MODE_PRINT_WINNER_FLAGS, CONTEST_DEBUG_MODE_PRINT_LOSER_FLAGS }; #define MOVE_WINDOWS_START WIN_MOVE0 #define TAG_CONTEST_SYMBOLS_PAL 0xABE0 #define TAG_JUDGE_SYMBOLS_GFX 0xABE0 #define TAG_FACES_GFX 0xABE1 #define TAG_APPLAUSE_METER 0xABE2 #define TAG_SLIDER_HEART 0x4E20 #define TAG_JUDGE 0x4E21 #define TAG_NEXT_TURN_PAL 0x4E22 #define TAG_NEXT_TURN_1_GFX 0x4E22 #define TAG_NEXT_TURN_2_GFX 0x4E23 #define TAG_NEXT_TURN_3_GFX 0x4E24 #define TAG_NEXT_TURN_4_GFX 0x4E25 #define TAG_BLINK_EFFECT_CONTESTANT0 0x80E8 #define TAG_BLINK_EFFECT_CONTESTANT1 0x80E9 #define TAG_BLINK_EFFECT_CONTESTANT2 0x80EA #define TAG_BLINK_EFFECT_CONTESTANT3 0x80EB #define TILE_FILLED_APPEAL_HEART 0x5012 #define TILE_FILLED_JAM_HEART 0x5014 #define TILE_EMPTY_APPEAL_HEART 0x5035 #define TILE_EMPTY_JAM_HEART 0x5036 enum { SLIDER_HEART_ANIM_NORMAL, SLIDER_HEART_ANIM_DISAPPEAR, SLIDER_HEART_ANIM_APPEAR, }; // States for Task_DoAppeals enum { APPEALSTATE_START_TURN, APPEALSTATE_WAIT_LINK, APPEALSTATE_CHECK_SKIP_TURN, APPEALSTATE_SLIDE_MON_IN, APPEALSTATE_WAIT_SLIDE_MON, APPEALSTATE_PRINT_USED_MOVE_MSG, APPEALSTATE_WAIT_USED_MOVE_MSG, APPEALSTATE_MOVE_ANIM, APPEALSTATE_WAIT_MOVE_ANIM, APPEALSTATE_MOVE_ANIM_MULTITURN, APPEALSTATE_SLIDE_MON_OUT, APPEALSTATE_FREE_MON_SPRITE, APPEALSTATE_UPDATE_MOVE_USERS_HEARTS, APPEALSTATE_WAIT_MOVE_USERS_HEARTS, APPEALSTATE_PRINT_COMBO_MSG, APPEALSTATE_TRY_UPDATE_HEARTS_FROM_COMBO, APPEALSTATE_WAIT_HEARTS_FROM_COMBO, APPEALSTATE_CHECK_REPEATED_MOVE, APPEALSTATE_WAIT_HEARTS_FROM_REPEAT, APPEALSTATE_UPDATE_HEARTS_FROM_REPEAT, APPEALSTATE_START_TURN_END_DELAY, APPEALSTATE_TURN_END_DELAY, APPEALSTATE_START_NEXT_TURN, APPEALSTATE_TRY_PRINT_MOVE_RESULT, APPEALSTATE_WAIT_MOVE_RESULT_MSG, APPEALSTATE_UPDATE_OPPONENTS, APPEALSTATE_UPDATE_OPPONENT, APPEALSTATE_WAIT_OPPONENT_RESPONSE_MSG, APPEALSTATE_UPDATE_OPPONENT_HEARTS, APPEALSTATE_WAIT_OPPONENT_HEARTS, APPEALSTATE_UPDATE_OPPONENT_STATUS, APPEALSTATE_PRINT_SKIP_TURN_MSG, APPEALSTATE_WAIT_SKIP_TURN_MSG, APPEALSTATE_PRINT_TOO_NERVOUS_MSG, APPEALSTATE_WAIT_TOO_NERVOUS_MSG, APPEALSTATE_TRY_JUDGE_STAR, APPEALSTATE_WAIT_JUDGE_STAR, APPEALSTATE_UPDATE_MOVE_USERS_STARS, APPEALSTATE_WAIT_MOVE_USERS_STARS, APPEALSTATE_UPDATE_OPPONENT_STARS, APPEALSTATE_WAIT_OPPONENT_STARS, APPEALSTATE_UPDATE_CROWD, APPEALSTATE_42, // Unused state APPEALSTATE_WAIT_EXCITEMENT_HEARTS, APPEALSTATE_44, // Unused state APPEALSTATE_WAIT_JUDGE_COMBO, APPEALSTATE_WAIT_JUDGE_REPEATED_MOVE, APPEALSTATE_TRY_SHOW_NEXT_TURN_GFX, APPEALSTATE_CHECK_TURN_ORDER_MOD, APPEALSTATE_WAIT_JUDGE_TURN_ORDER, APPEALSTATE_UPDATE_MOVE_USERS_STATUS, APPEALSTATE_TRY_PRINT_SKIP_NEXT_TURN_MSG, APPEALSTATE_WAIT_SKIP_NEXT_TURN_MSG, APPEALSTATE_DO_CROWD_UNEXCITED, APPEALSTATE_DO_CROWD_EXCITED, APPEALSTATE_SLIDE_APPLAUSE_OUT, APPEALSTATE_WAIT_SLIDE_APPLAUSE, APPEALSTATE_PRINT_CROWD_WATCHES_MSG, APPEALSTATE_PRINT_MON_MOVE_IGNORED_MSG, APPEALSTATE_WAIT_MON_MOVE_IGNORED_MSG, }; // EWRAM vars. EWRAM_DATA struct ContestPokemon gContestMons[CONTESTANT_COUNT] = {0}; EWRAM_DATA s16 gContestMonRound1Points[CONTESTANT_COUNT] = {0}; // "Round 1" points are based on condition EWRAM_DATA s16 gContestMonTotalPoints[CONTESTANT_COUNT] = {0}; // Round 1 points + Round 2 points EWRAM_DATA s16 gContestMonAppealPointTotals[CONTESTANT_COUNT] = {0}; EWRAM_DATA s16 gContestMonRound2Points[CONTESTANT_COUNT] = {0}; // "Round 2" points are just appeal points * 2 EWRAM_DATA u8 gContestFinalStandings[CONTESTANT_COUNT] = {0}; EWRAM_DATA u8 gContestMonPartyIndex = 0; EWRAM_DATA u8 gContestPlayerMonIndex = 0; EWRAM_DATA u8 gContestantTurnOrder[CONTESTANT_COUNT] = {0}; EWRAM_DATA u8 gLinkContestFlags = 0; // Bit 0: Is a link contest // Bit 1: Link contest uses wireless adapter EWRAM_DATA u8 gContestLinkLeaderIndex = 0; EWRAM_DATA u16 gSpecialVar_ContestCategory = 0; EWRAM_DATA u16 gSpecialVar_ContestRank = 0; EWRAM_DATA u8 gNumLinkContestPlayers = 0; EWRAM_DATA u8 gHighestRibbonRank = 0; EWRAM_DATA struct ContestResources *gContestResources = NULL; EWRAM_DATA u8 sContestBgCopyFlags = 0; EWRAM_DATA struct ContestWinner gCurContestWinner = {0}; EWRAM_DATA bool8 gCurContestWinnerIsForArtist = 0; EWRAM_DATA u8 gCurContestWinnerSaveIdx = 0; // IWRAM common vars. u32 gContestRngValue; extern const u8 gText_LinkStandby4[]; extern const u8 gText_BDot[]; extern const u8 gText_CDot[]; extern void (*const gContestEffectFuncs[])(void); static const u8 sSliderHeartYPositions[CONTESTANT_COUNT] = { 36, 76, 116, 156 }; // The "Next Turn" sprites cover up the slider, so the y positions are the same as above static const u8 sNextTurnSpriteYPositions[CONTESTANT_COUNT] = { 36, 76, 116, 156 }; static const struct SpriteSheet sSpriteSheet_SliderHeart = { .data = gContestSliderHeart_Gfx, .size = 0x20, .tag = TAG_SLIDER_HEART }; static const struct OamData sOam_SliderHeart = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(8x8), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(8x8), .tileNum = 0, .priority = 0, .paletteNum = 0, .affineParam = 0 }; static const union AffineAnimCmd sAffineAnim_SliderHeart_Normal[] = { AFFINEANIMCMD_FRAME(256, 256, 0, 0), AFFINEANIMCMD_END, }; static const union AffineAnimCmd sAffineAnim_SliderHeart_SpinDisappear[] = { AFFINEANIMCMD_FRAME(256, 256, 0, 0), AFFINEANIMCMD_FRAME(-10, -10, -20, 20), AFFINEANIMCMD_END }; static const union AffineAnimCmd sAffineAnim_SliderHeart_SpinAppear[] = { AFFINEANIMCMD_FRAME(56, 56, 0, 0), AFFINEANIMCMD_FRAME(10, 10, 20, 20), AFFINEANIMCMD_END }; static const union AffineAnimCmd* const sAffineAnims_SliderHeart[] = { [SLIDER_HEART_ANIM_NORMAL] = sAffineAnim_SliderHeart_Normal, [SLIDER_HEART_ANIM_DISAPPEAR] = sAffineAnim_SliderHeart_SpinDisappear, [SLIDER_HEART_ANIM_APPEAR] = sAffineAnim_SliderHeart_SpinAppear }; static const struct SpriteTemplate sSpriteTemplate_SliderHeart = { .tileTag = TAG_SLIDER_HEART, .paletteTag = TAG_CONTEST_SYMBOLS_PAL, .oam = &sOam_SliderHeart, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sAffineAnims_SliderHeart, .callback = SpriteCallbackDummy }; static const struct CompressedSpriteSheet sSpriteSheet_NextTurn[CONTESTANT_COUNT] = { { .data = gContestNextTurnGfx, .size = 0x100, .tag = TAG_NEXT_TURN_1_GFX }, { .data = gContestNextTurnGfx, .size = 0x100, .tag = TAG_NEXT_TURN_2_GFX }, { .data = gContestNextTurnGfx, .size = 0x100, .tag = TAG_NEXT_TURN_3_GFX }, { .data = gContestNextTurnGfx, .size = 0x100, .tag = TAG_NEXT_TURN_4_GFX } }; static const struct SpritePalette sSpritePalette_NextTurn = { .data = gContestPal, .tag = TAG_NEXT_TURN_PAL }; static const struct OamData sOam_NextTurn = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(32x8), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(32x8), .tileNum = 0, .priority = 0, .paletteNum = 0, .affineParam = 0 }; static const struct SpriteTemplate sSpriteTemplates_NextTurn[CONTESTANT_COUNT] = { { .tileTag = TAG_NEXT_TURN_1_GFX, .paletteTag = TAG_NEXT_TURN_PAL, .oam = &sOam_NextTurn, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }, { .tileTag = TAG_NEXT_TURN_2_GFX, .paletteTag = TAG_NEXT_TURN_PAL, .oam = &sOam_NextTurn, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }, { .tileTag = TAG_NEXT_TURN_3_GFX, .paletteTag = TAG_NEXT_TURN_PAL, .oam = &sOam_NextTurn, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }, { .tileTag = TAG_NEXT_TURN_4_GFX, .paletteTag = TAG_NEXT_TURN_PAL, .oam = &sOam_NextTurn, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, } }; static const struct Subsprite sSubsprites_NextTurn[] = { { .x = -28, .y = -4, .shape = SPRITE_SHAPE(32x8), .size = SPRITE_SIZE(32x8), .tileOffset = 0, .priority = 0 }, { .x = 4, .y = -4, .shape = SPRITE_SHAPE(32x8), .size = SPRITE_SIZE(32x8), .tileOffset = 4, .priority = 0 } }; static const struct SubspriteTable sSubspriteTable_NextTurn[] = { { .subspriteCount = ARRAY_COUNT(sSubsprites_NextTurn), .subsprites = sSubsprites_NextTurn } }; // Unused static const struct CompressedSpriteSheet sSpriteSheet_Faces = { .data = gContestFaces_Gfx, .size = 0x180, .tag = TAG_FACES_GFX }; static const struct OamData sOam_Faces = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(16x16), .x = 0, .size = SPRITE_SIZE(16x16), .tileNum = 0, .priority = 0, .paletteNum = 0, }; // Unused static const struct SpriteTemplate sSpriteTemplate_Faces = { .tileTag = TAG_FACES_GFX, .paletteTag = TAG_CONTEST_SYMBOLS_PAL, .oam = &sOam_Faces, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy }; static const struct CompressedSpriteSheet sSpriteSheet_ApplauseMeter = { .data = gContestApplauseGfx, .size = 0x400, .tag = TAG_APPLAUSE_METER }; static const struct SpritePalette sSpritePalette_ApplauseMeter = { .data = gContestPal, .tag = TAG_APPLAUSE_METER }; static const struct OamData sOam_ApplauseMeter = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(64x32), .x = 0, .size = SPRITE_SIZE(64x32), .tileNum = 0, .priority = 0, .paletteNum = 0, }; static const struct SpriteTemplate sSpriteTemplate_ApplauseMeter = { .tileTag = TAG_APPLAUSE_METER, .paletteTag = TAG_APPLAUSE_METER, .oam = &sOam_ApplauseMeter, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy }; const struct OamData sOam_Judge = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(64x64), .x = 0, .size = SPRITE_SIZE(64x64), .tileNum = 0, .priority = 3, .paletteNum = 2, }; const struct SpriteTemplate sSpriteTemplate_Judge = { .tileTag = TAG_JUDGE, .paletteTag = TAG_JUDGE, .oam = &sOam_Judge, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy }; const struct CompressedSpriteSheet sSpriteSheet_Judge = { .data = gContestJudgeGfx, .size = 0x800, .tag = TAG_JUDGE }; static const struct CompressedSpriteSheet sSpriteSheet_JudgeSymbols = { .data = gContestJudgeSymbolsGfx, .size = 0x380, .tag = TAG_JUDGE_SYMBOLS_GFX }; const struct CompressedSpritePalette sSpritePalette_JudgeSymbols = { .data = gContestJudgeSymbolsPal, .tag = TAG_CONTEST_SYMBOLS_PAL }; const struct SpriteTemplate sSpriteTemplate_JudgeSpeechBubble = { .tileTag = TAG_JUDGE_SYMBOLS_GFX, .paletteTag = TAG_CONTEST_SYMBOLS_PAL, .oam = &gOamData_AffineOff_ObjNormal_16x16, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy }; static const u16 sText_Pal[] = INCBIN_U16("graphics/contest/text.gbapal"); #include "data/contest_text_tables.h" static const struct BgTemplate sContestBgTemplates[] = { { .bg = 0, .charBaseIndex = 0, .mapBaseIndex = 0x18, .screenSize = 2, .paletteMode = 0, .priority = 0, .baseTile = 0 }, { .bg = 1, .charBaseIndex = 2, .mapBaseIndex = 0x1E, .screenSize = 2, .paletteMode = 0, .priority = 1, .baseTile = 0 }, { .bg = 2, .charBaseIndex = 0, .mapBaseIndex = 0x1C, .screenSize = 2, .paletteMode = 0, .priority = 0, .baseTile = 0 }, { .bg = 3, .charBaseIndex = 0, .mapBaseIndex = 0x1A, .screenSize = 0, .paletteMode = 0, .priority = 3, .baseTile = 0 } }; static const struct WindowTemplate sContestWindowTemplates[] = { [WIN_CONTESTANT0_NAME] = { .bg = 0, .tilemapLeft = 18, .tilemapTop = 0, .width = 12, .height = 2, .paletteNum = 0xF, .baseBlock = 0x200 }, [WIN_CONTESTANT1_NAME] = { .bg = 0, .tilemapLeft = 18, .tilemapTop = 5, .width = 12, .height = 2, .paletteNum = 0xF, .baseBlock = 0x218 }, [WIN_CONTESTANT2_NAME] = { .bg = 0, .tilemapLeft = 18, .tilemapTop = 10, .width = 12, .height = 2, .paletteNum = 0xF, .baseBlock = 0x230 }, [WIN_CONTESTANT3_NAME] = { .bg = 0, .tilemapLeft = 18, .tilemapTop = 15, .width = 12, .height = 2, .paletteNum = 0xF, .baseBlock = 0x248 }, [WIN_GENERAL_TEXT] = { .bg = 0, .tilemapLeft = 1, .tilemapTop = 15, .width = 17, .height = 4, .paletteNum = 0xF, .baseBlock = 0x260 }, [WIN_MOVE0] = { .bg = 0, .tilemapLeft = 1, .tilemapTop = 31, .width = 9, .height = 2, .paletteNum = 0xF, .baseBlock = 0x2A4 }, [WIN_MOVE1] = { .bg = 0, .tilemapLeft = 1, .tilemapTop = 33, .width = 9, .height = 2, .paletteNum = 0xF, .baseBlock = 0x2B6 }, [WIN_MOVE2] = { .bg = 0, .tilemapLeft = 1, .tilemapTop = 35, .width = 9, .height = 2, .paletteNum = 0xF, .baseBlock = 0x2C8 }, [WIN_MOVE3] = { .bg = 0, .tilemapLeft = 1, .tilemapTop = 37, .width = 9, .height = 2, .paletteNum = 0xF, .baseBlock = 0x2DA }, [WIN_SLASH] = { .bg = 0, .tilemapLeft = 16, .tilemapTop = 31, .width = 1, .height = 2, .paletteNum = 0xF, .baseBlock = 0x2EC }, [WIN_MOVE_DESCRIPTION] = { .bg = 0, .tilemapLeft = 11, .tilemapTop = 35, .width = 18, .height = 4, .paletteNum = 0xF, .baseBlock = 0x2EE }, DUMMY_WIN_TEMPLATE }; #include "data/contest_opponents.h" static const struct CompressedSpriteSheet sSpriteSheets_ContestantsTurnBlinkEffect[CONTESTANT_COUNT] = { { .data = gBlankGfxCompressed, .size = 0x1000, .tag = TAG_BLINK_EFFECT_CONTESTANT0 }, { .data = gBlankGfxCompressed, .size = 0x1000, .tag = TAG_BLINK_EFFECT_CONTESTANT1 }, { .data = gBlankGfxCompressed, .size = 0x1000, .tag = TAG_BLINK_EFFECT_CONTESTANT2 }, { .data = gBlankGfxCompressed, .size = 0x1000, .tag = TAG_BLINK_EFFECT_CONTESTANT3 } }; // Yup this is super dangerous but that's how it is here static const struct SpritePalette sSpritePalettes_ContestantsTurnBlinkEffect[CONTESTANT_COUNT] = { { .data = (u16*)(gHeap + 0x1A0A4), .tag = TAG_BLINK_EFFECT_CONTESTANT0 }, { .data = (u16*)(gHeap + 0x1A0C4), .tag = TAG_BLINK_EFFECT_CONTESTANT1 }, { .data = (u16*)(gHeap + 0x1A0E4), .tag = TAG_BLINK_EFFECT_CONTESTANT2 }, { .data = (u16*)(gHeap + 0x1A104), .tag = TAG_BLINK_EFFECT_CONTESTANT3 } }; const struct OamData sOam_ContestantsTurnBlinkEffect = { .y = 0, .affineMode = ST_OAM_AFFINE_DOUBLE, .objMode = ST_OAM_OBJ_BLEND, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(64x64), .x = 0, .size = SPRITE_SIZE(64x64), .tileNum = 0, .priority = 0, .paletteNum = 0, .affineParam = 0, }; const union AffineAnimCmd sAffineAnim_ContestantsTurnBlinkEffect_0[] = { AFFINEANIMCMD_FRAME(0x100, 0x100, 0, 0), AFFINEANIMCMD_END }; const union AffineAnimCmd sAffineAnim_ContestantsTurnBlinkEffect_1[] = { AFFINEANIMCMD_FRAME(3, 3, 0, 15), AFFINEANIMCMD_FRAME(-3, -3, 0, 15), AFFINEANIMCMD_FRAME(3, 3, 0, 15), AFFINEANIMCMD_FRAME(-3, -3, 0, 15), AFFINEANIMCMD_END }; const union AffineAnimCmd *const sAffineAnims_ContestantsTurnBlinkEffect[] = { sAffineAnim_ContestantsTurnBlinkEffect_0, sAffineAnim_ContestantsTurnBlinkEffect_1 }; const struct SpriteTemplate sSpriteTemplates_ContestantsTurnBlinkEffect[CONTESTANT_COUNT] = { { .tileTag = TAG_BLINK_EFFECT_CONTESTANT0, .paletteTag = TAG_BLINK_EFFECT_CONTESTANT0, .oam = &sOam_ContestantsTurnBlinkEffect, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sAffineAnims_ContestantsTurnBlinkEffect, .callback = SpriteCallbackDummy }, { .tileTag = TAG_BLINK_EFFECT_CONTESTANT1, .paletteTag = TAG_BLINK_EFFECT_CONTESTANT1, .oam = &sOam_ContestantsTurnBlinkEffect, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sAffineAnims_ContestantsTurnBlinkEffect, .callback = SpriteCallbackDummy }, { .tileTag = TAG_BLINK_EFFECT_CONTESTANT2, .paletteTag = TAG_BLINK_EFFECT_CONTESTANT2, .oam = &sOam_ContestantsTurnBlinkEffect, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sAffineAnims_ContestantsTurnBlinkEffect, .callback = SpriteCallbackDummy }, { .tileTag = TAG_BLINK_EFFECT_CONTESTANT3, .paletteTag = TAG_BLINK_EFFECT_CONTESTANT3, .oam = &sOam_ContestantsTurnBlinkEffect, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sAffineAnims_ContestantsTurnBlinkEffect, .callback = SpriteCallbackDummy } }; static const s8 gContestExcitementTable[CONTEST_CATEGORIES_COUNT][CONTEST_CATEGORIES_COUNT] = { [CONTEST_CATEGORY_COOL] = { [CONTEST_CATEGORY_COOL] = +1, [CONTEST_CATEGORY_BEAUTY] = 0, [CONTEST_CATEGORY_CUTE] = -1, [CONTEST_CATEGORY_SMART] = -1, [CONTEST_CATEGORY_TOUGH] = 0 }, [CONTEST_CATEGORY_BEAUTY] = { [CONTEST_CATEGORY_COOL] = 0, [CONTEST_CATEGORY_BEAUTY] = +1, [CONTEST_CATEGORY_CUTE] = 0, [CONTEST_CATEGORY_SMART] = -1, [CONTEST_CATEGORY_TOUGH] = -1 }, [CONTEST_CATEGORY_CUTE] = { [CONTEST_CATEGORY_COOL] = -1, [CONTEST_CATEGORY_BEAUTY] = 0, [CONTEST_CATEGORY_CUTE] = +1, [CONTEST_CATEGORY_SMART] = 0, [CONTEST_CATEGORY_TOUGH] = -1 }, [CONTEST_CATEGORY_SMART] = { [CONTEST_CATEGORY_COOL] = -1, [CONTEST_CATEGORY_BEAUTY] = -1, [CONTEST_CATEGORY_CUTE] = 0, [CONTEST_CATEGORY_SMART] = +1, [CONTEST_CATEGORY_TOUGH] = 0 }, [CONTEST_CATEGORY_TOUGH] = { [CONTEST_CATEGORY_COOL] = 0, [CONTEST_CATEGORY_BEAUTY] = -1, [CONTEST_CATEGORY_CUTE] = -1, [CONTEST_CATEGORY_SMART] = 0, [CONTEST_CATEGORY_TOUGH] = +1 } }; static void TaskDummy1(u8 taskId) { } void ResetLinkContestBoolean(void) { gLinkContestFlags = 0; } static void SetupContestGpuRegs(void) { SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_1D_MAP); SetGpuReg(REG_OFFSET_BLDCNT, 0); SetGpuReg(REG_OFFSET_BLDALPHA, 0); SetGpuReg(REG_OFFSET_BLDY, 0); SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_BG0 | WININ_WIN0_BG1 | WININ_WIN0_BG2 | WININ_WIN0_BG3 | WININ_WIN0_OBJ | WININ_WIN0_CLR | WININ_WIN1_BG0 | WININ_WIN1_BG1 | WININ_WIN1_BG2 | WININ_WIN1_BG3 | WININ_WIN1_OBJ | WININ_WIN1_CLR); SetGpuReg(REG_OFFSET_WINOUT, WINOUT_WIN01_BG0 | WINOUT_WIN01_BG1 | WINOUT_WIN01_BG2 | WINOUT_WIN01_BG3 | WINOUT_WIN01_OBJ | WINOUT_WIN01_CLR | WINOUT_WINOBJ_BG0 | WINOUT_WINOBJ_BG1 | WINOUT_WINOBJ_BG2 | WINOUT_WINOBJ_BG3 | WINOUT_WINOBJ_OBJ | WINOUT_WINOBJ_CLR); SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_BG0_ON | DISPCNT_BG1_ON | DISPCNT_BG2_ON | DISPCNT_BG3_ON | DISPCNT_OBJ_ON | DISPCNT_WIN0_ON | DISPCNT_WIN1_ON); gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gBattle_BG1_X = 0; gBattle_BG1_Y = 0; gBattle_BG2_X = 0; gBattle_BG2_Y = 0; gBattle_BG3_X = 0; gBattle_BG3_Y = 0; gBattle_WIN0H = 0; gBattle_WIN0V = 0; gBattle_WIN1H = 0; gBattle_WIN1V = 0; } void LoadContestBgAfterMoveAnim(void) { s32 i; LZDecompressVram(gContestInterfaceGfx, (void *)VRAM); LZDecompressVram(gContestAudienceGfx, (void *)(BG_SCREEN_ADDR(4))); CopyToBgTilemapBuffer(3, gContestAudienceTilemap, 0, 0); CopyBgTilemapBufferToVram(3); LoadCompressedPalette(gContestInterfaceAudiencePalette, 0, 0x200); LoadContestPalettes(); for (i = 0; i < CONTESTANT_COUNT; i++) { u32 contestantWindowId = 5 + i; LoadPalette(eContestTempSave.cachedWindowPalettes[contestantWindowId], 16 * (5 + gContestantTurnOrder[i]), sizeof((eContestTempSave.cachedWindowPalettes[contestantWindowId]))); } } static void InitContestInfoBgs(void) { s32 i; ResetBgsAndClearDma3BusyFlags(0); InitBgsFromTemplates(0, sContestBgTemplates, ARRAY_COUNT(sContestBgTemplates)); SetBgAttribute(3, BG_ATTR_WRAPAROUND, 1); for (i = 0; i < CONTESTANT_COUNT; i++) { SetBgTilemapBuffer(i, gContestResources->contestBgTilemaps[i]); } } static void InitContestWindows(void) { InitWindows(sContestWindowTemplates); DeactivateAllTextPrinters(); if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) { gTextFlags.canABSpeedUpPrint = FALSE; } else { gTextFlags.canABSpeedUpPrint = TRUE; } } static void LoadContestPalettes(void) { s32 i; LoadPalette(sText_Pal, 0xf0, 0x20); FillPalette(RGB_BLACK, 0, 2); for (i = 10; i < 14; i++) LoadPalette(gPlttBufferUnfaded + 241, 240 + i, 2); FillPalette(RGB(31, 17, 31), 0xF3, 2); } static void InitContestResources(void) { s32 i; eContest = (struct Contest){}; for (i = 0; i < CONTESTANT_COUNT; i++) { eContest.unk[i] = 0xFF; } for (i = 0; i < CONTESTANT_COUNT; i++) { eContestantStatus[i] = (struct ContestantStatus){}; } for (i = 0; i < CONTESTANT_COUNT; i++) { eContestantStatus[i].ranking = 0; eContestantStatus[i].effectStringId = CONTEST_STRING_NONE; eContestantStatus[i].effectStringId2 = CONTEST_STRING_NONE; } eContestAppealResults = (struct ContestAppealMoveResults){}; eContestAI = (struct ContestAIInfo){}; *gContestResources->excitement = (struct ContestExcitement){}; memset(eContestGfxState, 0, CONTESTANT_COUNT * sizeof(struct ContestGraphicsState)); if (!(gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK)) SortContestants(FALSE); for (i = 0; i < CONTESTANT_COUNT; i++) { eContestantStatus[i].nextTurnOrder = 0xFF; eContest.prevTurnOrder[i] = gContestantTurnOrder[i]; } // Calling this here while all the nextTurnOrder values are 0xFF will actually // just reverse the turn order. ApplyNextTurnOrder(); memset(gContestResources->tv, 0, sizeof(*gContestResources->tv) * CONTESTANT_COUNT); } static void AllocContestResources(void) { gContestResources = AllocZeroed(sizeof(struct ContestResources)); gContestResources->contest = AllocZeroed(sizeof(struct Contest)); gContestResources->status = AllocZeroed(sizeof(struct ContestantStatus) * CONTESTANT_COUNT); gContestResources->appealResults = AllocZeroed(sizeof(struct ContestAppealMoveResults)); gContestResources->aiData = AllocZeroed(sizeof(struct ContestAIInfo)); gContestResources->excitement = AllocZeroed(sizeof(struct ContestExcitement) * CONTESTANT_COUNT); gContestResources->gfxState = AllocZeroed(sizeof(struct ContestGraphicsState) * CONTESTANT_COUNT); gContestResources->moveAnim = AllocZeroed(sizeof(struct ContestMoveAnimData)); gContestResources->tv = AllocZeroed(sizeof(struct ContestTV) * CONTESTANT_COUNT); gContestResources->unused = AllocZeroed(sizeof(struct ContestUnused)); gContestResources->contestBgTilemaps[0] = AllocZeroed(0x1000); gContestResources->contestBgTilemaps[1] = AllocZeroed(0x1000); gContestResources->contestBgTilemaps[2] = AllocZeroed(0x1000); gContestResources->contestBgTilemaps[3] = AllocZeroed(0x1000); gContestResources->boxBlinkTiles1 = AllocZeroed(0x800); gContestResources->boxBlinkTiles2 = AllocZeroed(0x800); gContestResources->animBgTileBuffer = AllocZeroed(0x2000); gBattleAnimBgTileBuffer = gContestResources->animBgTileBuffer; gBattleAnimBgTilemapBuffer = gContestResources->contestBgTilemaps[1]; } static void FreeContestResources(void) { FREE_AND_SET_NULL(gContestResources->contest); FREE_AND_SET_NULL(gContestResources->status); FREE_AND_SET_NULL(gContestResources->appealResults); FREE_AND_SET_NULL(gContestResources->aiData); FREE_AND_SET_NULL(gContestResources->excitement); FREE_AND_SET_NULL(gContestResources->gfxState); FREE_AND_SET_NULL(gContestResources->moveAnim); FREE_AND_SET_NULL(gContestResources->tv); FREE_AND_SET_NULL(gContestResources->unused); FREE_AND_SET_NULL(gContestResources->contestBgTilemaps[0]); FREE_AND_SET_NULL(gContestResources->contestBgTilemaps[1]); FREE_AND_SET_NULL(gContestResources->contestBgTilemaps[2]); FREE_AND_SET_NULL(gContestResources->contestBgTilemaps[3]); FREE_AND_SET_NULL(gContestResources->boxBlinkTiles1); FREE_AND_SET_NULL(gContestResources->boxBlinkTiles2); FREE_AND_SET_NULL(gContestResources->animBgTileBuffer); FREE_AND_SET_NULL(gContestResources); gBattleAnimBgTileBuffer = NULL; gBattleAnimBgTilemapBuffer = NULL; } void CB2_StartContest(void) { switch (gMain.state) { case 0: sContestBgCopyFlags = 0; AllocContestResources(); AllocateMonSpritesGfx(); FREE_AND_SET_NULL(gMonSpritesGfxPtr->firstDecompressed); gMonSpritesGfxPtr->firstDecompressed = AllocZeroed(0x4000); SetVBlankCallback(NULL); InitContestInfoBgs(); InitContestWindows(); SetupContestGpuRegs(); ScanlineEffect_Clear(); ResetPaletteFade(); gPaletteFade.bufferTransferDisabled = TRUE; ResetSpriteData(); ResetTasks(); FreeAllSpritePalettes(); gReservedSpritePaletteCount = 4; eContestDebugMode = CONTEST_DEBUG_MODE_OFF; ClearBattleMonForms(); InitContestResources(); gMain.state++; break; case 1: gMain.state++; break; case 2: if (SetupContestGraphics(&eContest.contestSetupState)) { eContest.contestSetupState = 0; gMain.state++; } break; case 3: SetBgForCurtainDrop(); gBattle_BG1_X = 0; gBattle_BG1_Y = 0; BeginFastPaletteFade(2); gPaletteFade.bufferTransferDisabled = FALSE; SetVBlankCallback(VBlankCB_Contest); eContest.mainTaskId = CreateTask(Task_StartContestWaitFade, 10); SetMainCallback2(CB2_ContestMain); if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_WIRELESS) { LoadWirelessStatusIndicatorSpriteGfx(); CreateWirelessStatusIndicatorSprite(8, 8); } break; } } static void Task_StartContestWaitFade(u8 taskId) { if (!gPaletteFade.active) { gTasks[taskId].data[0] = 0; gTasks[taskId].func = Task_TryStartLinkContest; } } // If this is a link contest try to start appeals communication // Otherwise skip ahead static void Task_TryStartLinkContest(u8 taskId) { if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) { if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_WIRELESS) { switch (gTasks[taskId].data[0]) { case 0: ContestPrintLinkStandby(); gTasks[taskId].data[0]++; // fallthrough case 1: if (IsLinkTaskFinished()) { SetLinkStandbyCallback(); gTasks[taskId].data[0]++; } return; case 2: if (IsLinkTaskFinished() != TRUE) return; gTasks[taskId].data[0]++; break; } } if (!gPaletteFade.active) { gPaletteFade.bufferTransferDisabled = FALSE; if (!(gLinkContestFlags & LINK_CONTEST_FLAG_IS_WIRELESS)) ContestPrintLinkStandby(); CreateTask(Task_CommunicateMonIdxs, 0); gTasks[taskId].data[0] = 0; gTasks[taskId].func = TaskDummy1; } } else { gTasks[taskId].func = Task_WaitToRaiseCurtainAtStart; } } static void Task_CommunicateMonIdxs(u8 taskId) { SetTaskFuncWithFollowupFunc(taskId, Task_LinkContest_CommunicateMonIdxs, Task_EndCommunicateMonIdxs); } static void Task_EndCommunicateMonIdxs(u8 taskId) { gTasks[taskId].data[0] = 1; gTasks[taskId].func = Task_ReadyStartLinkContest; } static void Task_ReadyStartLinkContest(u8 taskId) { // data[0] always 1 here gTasks[taskId].data[0]--; if (gTasks[taskId].data[0] <= 0) { GetMultiplayerId(); // unused return value DestroyTask(taskId); gTasks[eContest.mainTaskId].func = Task_WaitToRaiseCurtainAtStart; gRngValue = gContestRngValue; } } static bool8 SetupContestGraphics(u8 *stateVar) { u16 tempPalette1[16]; u16 tempPalette2[16]; switch (*stateVar) { case 0: gPaletteFade.bufferTransferDisabled = TRUE; RequestDma3Fill(0, (void *)VRAM, 0x8000, 1); RequestDma3Fill(0, (void *)VRAM + 0x8000, 0x8000, 1); RequestDma3Fill(0, (void *)VRAM + 0x10000, 0x8000, 1); break; case 1: LZDecompressVram(gContestInterfaceGfx, (void *)VRAM); break; case 2: LZDecompressVram(gContestAudienceGfx, (void *)(BG_SCREEN_ADDR(4))); DmaCopyLarge32(3, (void *)(BG_SCREEN_ADDR(4)), eUnzippedContestAudience_Gfx, 0x2000, 0x1000); break; case 3: CopyToBgTilemapBuffer(3, gContestAudienceTilemap, 0, 0); CopyBgTilemapBufferToVram(3); break; case 4: CopyToBgTilemapBuffer(2, gContestInterfaceTilemap, 0, 0); CopyBgTilemapBufferToVram(2); // This is a bug, and copies random junk. savedJunk is never read. DmaCopy32Defvars(3, gContestResources->contestBgTilemaps[2], eContestTempSave.savedJunk, sizeof(eContestTempSave.savedJunk)); break; case 5: LoadCompressedPalette(gContestInterfaceAudiencePalette, 0, 0x200); CpuCopy32(gPlttBufferUnfaded + 128, tempPalette1, 16 * sizeof(u16)); CpuCopy32(gPlttBufferUnfaded + (5 + gContestPlayerMonIndex) * 16, tempPalette2, 16 * sizeof(u16)); CpuCopy32(tempPalette2, gPlttBufferUnfaded + 128, 16 * sizeof(u16)); CpuCopy32(tempPalette1, gPlttBufferUnfaded + (5 + gContestPlayerMonIndex) * 16, 16 * sizeof(u16)); DmaCopy32Defvars(3, gPlttBufferUnfaded, eContestTempSave.cachedWindowPalettes, sizeof(eContestTempSave.cachedWindowPalettes)); LoadContestPalettes(); break; case 6: DrawContestantWindows(); FillContestantWindowBgs(); SwapMoveDescAndContestTilemaps(); eContest.judgeSpeechBubbleSpriteId = CreateJudgeSpeechBubbleSprite(); CreateSliderHeartSprites(); CreateNextTurnSprites(); CreateApplauseMeterSprite(); CreateJudgeAttentionEyeTask(); CreateUnusedBlendTask(); gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; gBattlerPositions[2] = B_POSITION_OPPONENT_RIGHT; gBattlerPositions[3] = B_POSITION_PLAYER_RIGHT; gBattleTypeFlags = 0; gBattlerAttacker = B_POSITION_PLAYER_RIGHT; gBattlerTarget = B_POSITION_OPPONENT_RIGHT; // Unclear why judge sprite is assigned here // Overwritten in APPEALSTATE_SLIDE_MON_IN with the attacking contest mon gBattlerSpriteIds[gBattlerAttacker] = CreateJudgeSprite(); CreateInvisibleBattleTargetSprite(); CopyBgTilemapBufferToVram(3); CopyBgTilemapBufferToVram(2); CopyBgTilemapBufferToVram(1); ShowBg(3); ShowBg(2); ShowBg(0); ShowBg(1); break; default: *stateVar = 0; return TRUE; } (*stateVar)++; return FALSE; } static void Task_WaitToRaiseCurtainAtStart(u8 taskId) { gPaletteFade.bufferTransferDisabled = FALSE; if (!gPaletteFade.active) { gTasks[taskId].data[0] = 0; gTasks[taskId].data[1] = 0; gTasks[taskId].func = Task_RaiseCurtainAtStart; } } static void Task_RaiseCurtainAtStart(u8 taskId) { switch (gTasks[taskId].data[0]) { case 0: if (gTasks[taskId].data[1]++ <= 60) break; gTasks[taskId].data[1] = 0; PlaySE12WithPanning(SE_CONTEST_CURTAIN_RISE, 0); gTasks[taskId].data[0]++; break; case 1: *(s16*)&gBattle_BG1_Y += 7; if ((s16)gBattle_BG1_Y <= DISPLAY_HEIGHT) break; gTasks[taskId].data[0]++; break; case 2: UpdateContestantBoxOrder(); gTasks[taskId].data[0]++; break; case 3: { u16 bg0Cnt = GetGpuReg(REG_OFFSET_BG0CNT); u16 bg2Cnt = GetGpuReg(REG_OFFSET_BG2CNT); ((struct BgCnt *)&bg0Cnt)->priority = 0; ((struct BgCnt *)&bg2Cnt)->priority = 0; SetGpuReg(REG_OFFSET_BG0CNT, bg0Cnt); SetGpuReg(REG_OFFSET_BG2CNT, bg2Cnt); SlideApplauseMeterIn(); gTasks[taskId].data[0]++; break; } case 4: default: if (eContest.applauseMeterIsMoving) break; gTasks[taskId].data[0] = 0; gTasks[taskId].data[1] = 0; gTasks[taskId].func = Task_DisplayAppealNumberText; break; } } static void CB2_ContestMain(void) { s32 i; AnimateSprites(); RunTasks(); BuildOamBuffer(); UpdatePaletteFade(); for (i = 0; i < 4; i++) { if ((sContestBgCopyFlags >> i) & 1) CopyBgTilemapBufferToVram(i); } sContestBgCopyFlags = 0; } static void VBlankCB_Contest(void) { SetGpuReg(REG_OFFSET_BG0HOFS, gBattle_BG0_X); SetGpuReg(REG_OFFSET_BG0VOFS, gBattle_BG0_Y); SetGpuReg(REG_OFFSET_BG1HOFS, gBattle_BG1_X); SetGpuReg(REG_OFFSET_BG1VOFS, gBattle_BG1_Y); SetGpuReg(REG_OFFSET_BG2HOFS, gBattle_BG2_X); SetGpuReg(REG_OFFSET_BG2VOFS, gBattle_BG2_Y); SetGpuReg(REG_OFFSET_BG3HOFS, gBattle_BG3_X); SetGpuReg(REG_OFFSET_BG3VOFS, gBattle_BG3_Y); SetGpuReg(REG_OFFSET_WIN0H, gBattle_WIN0H); SetGpuReg(REG_OFFSET_WIN0V, gBattle_WIN0V); SetGpuReg(REG_OFFSET_WIN1H, gBattle_WIN1H); SetGpuReg(REG_OFFSET_WIN1V, gBattle_WIN1V); TransferPlttBuffer(); LoadOam(); ProcessSpriteCopyRequests(); ScanlineEffect_InitHBlankDmaTransfer(); } static void Task_DisplayAppealNumberText(u8 taskId) { if (gTasks[taskId].data[0] == 0) { gBattle_BG0_Y = 0; gBattle_BG2_Y = 0; ContestDebugDoPrint(); DmaCopy32Defvars(3, gPlttBufferUnfaded, eContestTempSave.cachedPlttBufferUnfaded, PLTT_BUFFER_SIZE * 2); ConvertIntToDecimalStringN(gStringVar1, eContest.appealNumber + 1, STR_CONV_MODE_LEFT_ALIGN, 1); if (!Contest_IsMonsTurnDisabled(gContestPlayerMonIndex)) StringCopy(gDisplayedStringBattle, gText_AppealNumWhichMoveWillBePlayed); else StringCopy(gDisplayedStringBattle, gText_AppealNumButItCantParticipate); ContestClearGeneralTextWindow(); StringExpandPlaceholders(gStringVar4, gDisplayedStringBattle); Contest_StartTextPrinter(gStringVar4, TRUE); gTasks[taskId].data[0]++; } else { if (!Contest_RunTextPrinters()) { gTasks[taskId].data[0] = 0; gTasks[taskId].func = Task_TryShowMoveSelectScreen; } } } static void Task_TryShowMoveSelectScreen(u8 taskId) { // Wait for button press to show move select screen if ((JOY_NEW(A_BUTTON)) || (gMain.newKeys == B_BUTTON)) { PlaySE(SE_SELECT); if (!Contest_IsMonsTurnDisabled(gContestPlayerMonIndex)) { SetBottomSliderHeartsInvisibility(TRUE); gTasks[taskId].func = Task_ShowMoveSelectScreen; } else { // Skip move selection (selected move will be MOVE_NONE) gTasks[taskId].func = Task_SelectedMove; } } } static void Task_ShowMoveSelectScreen(u8 taskId) { u8 i; u8 moveName[32]; gBattle_BG0_Y = DISPLAY_HEIGHT; gBattle_BG2_Y = DISPLAY_HEIGHT; for (i = 0; i < MAX_MON_MOVES; i++) { u16 move = gContestMons[gContestPlayerMonIndex].moves[i]; u8 *moveNameBuffer = moveName; if (eContestantStatus[gContestPlayerMonIndex].prevMove != MOVE_NONE && IsContestantAllowedToCombo(gContestPlayerMonIndex) && AreMovesContestCombo(eContestantStatus[gContestPlayerMonIndex].prevMove, move) && eContestantStatus[gContestPlayerMonIndex].hasJudgesAttention) { // Highlight the text because it's a combo move moveNameBuffer = StringCopy(moveName, gText_ColorLightShadowDarkGray); } else if (move != MOVE_NONE && eContestantStatus[gContestPlayerMonIndex].prevMove == move && gContestMoves[move].effect != CONTEST_EFFECT_REPETITION_NOT_BORING) { // Gray the text because it's a repeated move moveNameBuffer = StringCopy(moveName, gText_ColorBlue); } moveNameBuffer = StringCopy(moveNameBuffer, gMoveNames[move]); FillWindowPixelBuffer(i + MOVE_WINDOWS_START, PIXEL_FILL(0)); Contest_PrintTextToBg0WindowAt(i + MOVE_WINDOWS_START, moveName, 5, 1, FONT_NARROW); } DrawMoveSelectArrow(eContest.playerMoveChoice); PrintContestMoveDescription(gContestMons[gContestPlayerMonIndex].moves[eContest.playerMoveChoice]); gTasks[taskId].func = Task_HandleMoveSelectInput; } static void Task_HandleMoveSelectInput(u8 taskId) { u8 numMoves = 0; s32 i; for (i = 0; i < MAX_MON_MOVES; i++) { if (gContestMons[gContestPlayerMonIndex].moves[i] != MOVE_NONE) numMoves++; } if (JOY_NEW(A_BUTTON)) { PlaySE(SE_SELECT); gTasks[taskId].func = Task_SelectedMove; } else { switch (gMain.newAndRepeatedKeys) { case B_BUTTON: // Cancel move selection PlaySE(SE_SELECT); SetBottomSliderHeartsInvisibility(FALSE); ConvertIntToDecimalStringN(gStringVar1, eContest.appealNumber + 1, STR_CONV_MODE_LEFT_ALIGN, 1); if (!Contest_IsMonsTurnDisabled(gContestPlayerMonIndex)) StringCopy(gDisplayedStringBattle, gText_AppealNumWhichMoveWillBePlayed); else StringCopy(gDisplayedStringBattle, gText_AppealNumButItCantParticipate); ContestClearGeneralTextWindow(); StringExpandPlaceholders(gStringVar4, gDisplayedStringBattle); Contest_StartTextPrinter(gStringVar4, 0); gBattle_BG0_Y = 0; gBattle_BG2_Y = 0; gTasks[taskId].func = Task_TryShowMoveSelectScreen; break; case DPAD_LEFT: case DPAD_RIGHT: break; case DPAD_UP: EraseMoveSelectArrow(eContest.playerMoveChoice); if (eContest.playerMoveChoice == 0) eContest.playerMoveChoice = numMoves - 1; else eContest.playerMoveChoice--; DrawMoveSelectArrow(eContest.playerMoveChoice); PrintContestMoveDescription(gContestMons[gContestPlayerMonIndex].moves[eContest.playerMoveChoice]); if (numMoves > 1) PlaySE(SE_SELECT); break; case DPAD_DOWN: EraseMoveSelectArrow(eContest.playerMoveChoice); if (eContest.playerMoveChoice == numMoves - 1) eContest.playerMoveChoice = 0; else eContest.playerMoveChoice++; DrawMoveSelectArrow(eContest.playerMoveChoice); PrintContestMoveDescription(gContestMons[gContestPlayerMonIndex].moves[eContest.playerMoveChoice]); if (numMoves > 1) PlaySE(SE_SELECT); break; } } } static void DrawMoveSelectArrow(s8 moveIndex) { ContestBG_FillBoxWithIncrementingTile(2, 55, 0, 31 + moveIndex * 2, 2, 2, 17, 1); } static void EraseMoveSelectArrow(s8 moveIndex) { ContestBG_FillBoxWithIncrementingTile(2, 11, 0, 31 + moveIndex * 2, 2, 1, 17, 1); ContestBG_FillBoxWithIncrementingTile(2, 11, 0, 32 + moveIndex * 2, 2, 1, 17, 1); } static void Task_SelectedMove(u8 taskId) { if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) { u16 move = GetChosenMove(gContestPlayerMonIndex); u8 taskId2; eContestantStatus[gContestPlayerMonIndex].currMove = move; taskId2 = CreateTask(Task_LinkContest_CommunicateMoveSelections, 0); SetTaskFuncWithFollowupFunc(taskId2, Task_LinkContest_CommunicateMoveSelections, Task_EndCommunicateMoveSelections); gTasks[taskId].func = TaskDummy1; ContestPrintLinkStandby(); SetBottomSliderHeartsInvisibility(FALSE); } else { GetAllChosenMoves(); gTasks[taskId].func = Task_HideMoveSelectScreen; } } static void Task_EndCommunicateMoveSelections(u8 taskId) { DestroyTask(taskId); gTasks[eContest.mainTaskId].func = Task_HideMoveSelectScreen; } static void Task_HideMoveSelectScreen(u8 taskId) { s32 i; ContestClearGeneralTextWindow(); gBattle_BG0_Y = 0; gBattle_BG2_Y = 0; SetBottomSliderHeartsInvisibility(FALSE); for (i = 0; i < MAX_MON_MOVES; i++) { FillWindowPixelBuffer(MOVE_WINDOWS_START + i, PIXEL_FILL(0)); PutWindowTilemap(MOVE_WINDOWS_START + i); CopyWindowToVram(MOVE_WINDOWS_START + i, COPYWIN_GFX); } Contest_SetBgCopyFlags(0); // This seems to be a bug; it should have just copied PLTT_BUFFER_SIZE. DmaCopy32Defvars(3, gPlttBufferFaded, eContestTempSave.cachedPlttBufferFaded, PLTT_BUFFER_SIZE * 2); LoadPalette(eContestTempSave.cachedPlttBufferUnfaded, 0, PLTT_BUFFER_SIZE * 2); gTasks[taskId].data[0] = 0; gTasks[taskId].data[1] = 0; gTasks[taskId].func = Task_HideApplauseMeterForAppealStart; } static void Task_HideApplauseMeterForAppealStart(u8 taskId) { if (++gTasks[taskId].data[0] > 2) { gTasks[taskId].data[0] = 0; if (++gTasks[taskId].data[1] == 2) { SlideApplauseMeterOut(); AnimateSliderHearts(SLIDER_HEART_ANIM_DISAPPEAR); gTasks[taskId].func = Task_WaitHideApplauseMeterForAppealStart; } } } static void Task_WaitHideApplauseMeterForAppealStart(u8 taskId) { if (!eContest.applauseMeterIsMoving && !eContest.sliderHeartsAnimating) gTasks[taskId].func = Task_AppealSetup; } #define tState data[0] #define tMonSpriteId data[2] #define tCounter data[10] static void Task_AppealSetup(u8 taskId) { if (++gTasks[taskId].data[0] > 19) { eContest.turnNumber = 0; eContest.unusedRng = gRngValue; if ((gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) && IsPlayerLinkLeader()) { s32 i; for (i = 0; i + gNumLinkContestPlayers < CONTESTANT_COUNT; i++) { eContestantStatus[gNumLinkContestPlayers + i].currMove = GetChosenMove(gNumLinkContestPlayers + i); } } gTasks[taskId].tState = APPEALSTATE_START_TURN; gTasks[taskId].func = Task_DoAppeals; } } static void Task_DoAppeals(u8 taskId) { u8 spriteId; s32 i; u8 contestant = eContest.currentContestant; s8 r3; switch (gTasks[taskId].tState) { case APPEALSTATE_START_TURN: ContestDebugDoPrint(); for (i = 0; eContest.turnNumber != eContestAppealResults.turnOrder[i]; i++) ; eContest.currentContestant = i; contestant = eContest.currentContestant; if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) { u8 taskId2; eContest.waitForLink = TRUE; if (IsPlayerLinkLeader()) CalculateAppealMoveImpact(eContest.currentContestant); taskId2 = CreateTask(Task_LinkContest_CommunicateAppealsState, 0); SetTaskFuncWithFollowupFunc(taskId2, Task_LinkContest_CommunicateAppealsState, Task_EndWaitForLink); ContestPrintLinkStandby(); gTasks[taskId].tState = APPEALSTATE_WAIT_LINK; } else { CalculateAppealMoveImpact(eContest.currentContestant); gTasks[taskId].tState = APPEALSTATE_CHECK_SKIP_TURN; } return; case APPEALSTATE_WAIT_LINK: if (!eContest.waitForLink) gTasks[taskId].tState = APPEALSTATE_CHECK_SKIP_TURN; return; case APPEALSTATE_CHECK_SKIP_TURN: SetContestLiveUpdateFlags(contestant); ContestDebugPrintBitStrings(); if (eContestantStatus[contestant].numTurnsSkipped != 0 || eContestantStatus[contestant].noMoreTurns) { gTasks[taskId].tState = APPEALSTATE_PRINT_SKIP_TURN_MSG; } else { ContestClearGeneralTextWindow(); gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_SLIDE_MON_IN; } return; case APPEALSTATE_SLIDE_MON_IN: for (i = 0; i < CONTESTANT_COUNT; i++) gBattleMonForms[i] = 0; memset(gContestResources->moveAnim, 0, sizeof(*gContestResources->moveAnim)); SetMoveAnimAttackerData(eContest.currentContestant); spriteId = CreateContestantSprite( gContestMons[eContest.currentContestant].species, gContestMons[eContest.currentContestant].otId, gContestMons[eContest.currentContestant].personality, eContest.currentContestant); gSprites[spriteId].x2 = 120; gSprites[spriteId].callback = SpriteCB_MonSlideIn; gTasks[taskId].tMonSpriteId = spriteId; gBattlerSpriteIds[gBattlerAttacker] = spriteId; BlinkContestantBox(CreateContestantBoxBlinkSprites(eContest.currentContestant), FALSE); gTasks[taskId].tState = APPEALSTATE_WAIT_SLIDE_MON; return; case APPEALSTATE_WAIT_SLIDE_MON: spriteId = gTasks[taskId].tMonSpriteId; if (gSprites[spriteId].callback == SpriteCallbackDummy) { // Once mon has slid in, also wait for box to finish blinking if (!eContestGfxState[contestant].boxBlinking) gTasks[taskId].tState = APPEALSTATE_PRINT_USED_MOVE_MSG; } return; case APPEALSTATE_PRINT_USED_MOVE_MSG: if (eContestantStatus[contestant].nervous) { gTasks[taskId].tState = APPEALSTATE_PRINT_TOO_NERVOUS_MSG; } else { ContestClearGeneralTextWindow(); StringCopy(gStringVar1, gContestMons[contestant].nickname); if (eContestantStatus[contestant].currMove < MOVES_COUNT) StringCopy(gStringVar2, gMoveNames[eContestantStatus[contestant].currMove]); else StringCopy(gStringVar2, sInvalidContestMoveNames[eContestantStatus[contestant].moveCategory]); StringExpandPlaceholders(gStringVar4, gText_MonAppealedWithMove); Contest_StartTextPrinter(gStringVar4, 1); gTasks[taskId].tState = APPEALSTATE_WAIT_USED_MOVE_MSG; } return; case APPEALSTATE_WAIT_USED_MOVE_MSG: if (!Contest_RunTextPrinters()) { eContest.moveAnimTurnCount = 0; gTasks[taskId].tState = APPEALSTATE_MOVE_ANIM; } return; case APPEALSTATE_MOVE_ANIM: { u16 move = SanitizeMove(eContestantStatus[eContest.currentContestant].currMove); SetMoveSpecificAnimData(eContest.currentContestant); SetMoveAnimAttackerData(eContest.currentContestant); SetMoveTargetPosition(move); DoMoveAnim(move); gTasks[taskId].tState = APPEALSTATE_WAIT_MOVE_ANIM; } return; case APPEALSTATE_WAIT_MOVE_ANIM: gAnimScriptCallback(); if (!gAnimScriptActive) { ClearMoveAnimData(contestant); if (eContest.moveAnimTurnCount != 0) { gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_MOVE_ANIM_MULTITURN; } else { if (!eContestantStatus[contestant].hasJudgesAttention) StopFlashJudgeAttentionEye(contestant); DrawUnnervedSymbols(); gTasks[taskId].tState = APPEALSTATE_TRY_PRINT_MOVE_RESULT; } } return; case APPEALSTATE_MOVE_ANIM_MULTITURN: if (gTasks[taskId].tCounter++ > 30) { gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_MOVE_ANIM; } return; case APPEALSTATE_TRY_PRINT_MOVE_RESULT: gTasks[taskId].data[1] = 0; if (eContestantStatus[contestant].effectStringId != CONTEST_STRING_NONE) { PrintAppealMoveResultText(contestant, eContestantStatus[contestant].effectStringId); eContestantStatus[contestant].effectStringId = CONTEST_STRING_NONE; gTasks[taskId].tState = APPEALSTATE_WAIT_MOVE_RESULT_MSG; } else { if (eContestantStatus[contestant].effectStringId2 != CONTEST_STRING_NONE) { for (i = 0; i < CONTESTANT_COUNT; i++) { if (i != contestant && eContestantStatus[i].effectStringId != CONTEST_STRING_NONE) break; } if (i == CONTESTANT_COUNT) { PrintAppealMoveResultText(contestant, eContestantStatus[contestant].effectStringId2); eContestantStatus[contestant].effectStringId2 = CONTEST_STRING_NONE; gTasks[taskId].tState = APPEALSTATE_WAIT_MOVE_RESULT_MSG; } else { gTasks[taskId].tState = APPEALSTATE_CHECK_TURN_ORDER_MOD; } } else { gTasks[taskId].tState = APPEALSTATE_CHECK_TURN_ORDER_MOD; } } return; case APPEALSTATE_WAIT_MOVE_RESULT_MSG: if (!Contest_RunTextPrinters()) gTasks[taskId].tState = APPEALSTATE_TRY_PRINT_MOVE_RESULT; return; case APPEALSTATE_CHECK_TURN_ORDER_MOD: if (eContestantStatus[contestant].turnOrderModAction == 1) { DoJudgeSpeechBubble(JUDGE_SYMBOL_NUMBER_ONE); } else if (eContestantStatus[contestant].turnOrderModAction == 2) { DoJudgeSpeechBubble(JUDGE_SYMBOL_NUMBER_FOUR); } else if (eContestantStatus[contestant].turnOrderModAction == 3) { DoJudgeSpeechBubble(JUDGE_SYMBOL_QUESTION_MARK); } else { gTasks[taskId].tState = APPEALSTATE_TRY_SHOW_NEXT_TURN_GFX; return; } gTasks[taskId].tState = APPEALSTATE_WAIT_JUDGE_TURN_ORDER; return; case APPEALSTATE_WAIT_JUDGE_TURN_ORDER: if (!eContest.waitForJudgeSpeechBubble) gTasks[taskId].tState = APPEALSTATE_TRY_SHOW_NEXT_TURN_GFX; return; case APPEALSTATE_TRY_SHOW_NEXT_TURN_GFX: ShowHideNextTurnGfx(TRUE); gTasks[taskId].tState = APPEALSTATE_UPDATE_MOVE_USERS_HEARTS; return; case APPEALSTATE_UPDATE_MOVE_USERS_HEARTS: UpdateAppealHearts(0, eContestantStatus[contestant].appeal, contestant); gTasks[taskId].tState = APPEALSTATE_WAIT_MOVE_USERS_HEARTS; return; case APPEALSTATE_WAIT_MOVE_USERS_HEARTS: if (!eContestGfxState[eContest.currentContestant].updatingAppealHearts) gTasks[taskId].tState = APPEALSTATE_TRY_JUDGE_STAR; return; case APPEALSTATE_TRY_JUDGE_STAR: if (eContestantStatus[contestant].conditionMod == CONDITION_GAIN) DoJudgeSpeechBubble(JUDGE_SYMBOL_STAR); gTasks[taskId].tState = APPEALSTATE_WAIT_JUDGE_STAR; return; case APPEALSTATE_WAIT_JUDGE_STAR: if (!eContest.waitForJudgeSpeechBubble) gTasks[taskId].tState = APPEALSTATE_UPDATE_MOVE_USERS_STARS; return; case APPEALSTATE_UPDATE_MOVE_USERS_STARS: if (UpdateConditionStars(contestant, TRUE)) { gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_WAIT_MOVE_USERS_STARS; } else { gTasks[taskId].tState = APPEALSTATE_UPDATE_MOVE_USERS_STATUS; } return; case APPEALSTATE_WAIT_MOVE_USERS_STARS: if (++gTasks[taskId].tCounter > 20) { gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_UPDATE_MOVE_USERS_STATUS; } return; case APPEALSTATE_UPDATE_MOVE_USERS_STATUS: if (DrawStatusSymbol(contestant)) PlaySE(SE_CONTEST_ICON_CHANGE); gTasks[taskId].tState = APPEALSTATE_UPDATE_OPPONENTS; return; case APPEALSTATE_UPDATE_OPPONENTS: gTasks[taskId].data[1] = 0; gTasks[taskId].tState = APPEALSTATE_UPDATE_OPPONENT; return; case APPEALSTATE_UPDATE_OPPONENT: { // Update each other contestant as a result of this move appeal s32 j = 0; r3 = FALSE; // Can't get this to use local variable. Should be "needsUpdate" for (i = gTasks[taskId].data[1]; i < CONTESTANT_COUNT; i++) { r3 = FALSE; for (j = 0; j < CONTESTANT_COUNT; j++) { if (j != contestant && gContestantTurnOrder[j] == i && eContestantStatus[j].effectStringId != CONTEST_STRING_NONE) { r3 = TRUE; break; } } if (r3) break; } if (r3) { // Update contestant gTasks[taskId].data[1] = gContestantTurnOrder[j]; PrintAppealMoveResultText(j, eContestantStatus[j].effectStringId); eContestantStatus[j].effectStringId = CONTEST_STRING_NONE; gTasks[taskId].tState = APPEALSTATE_WAIT_OPPONENT_RESPONSE_MSG; } else { // Done updating contestants gTasks[taskId].data[1] = 0; gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_TRY_PRINT_SKIP_NEXT_TURN_MSG; DrawStatusSymbols(); } } return; case APPEALSTATE_WAIT_OPPONENT_RESPONSE_MSG: // Wait for contestants response to current appeal // i.e. "Contestant managed to avert its gaze" if (!Contest_RunTextPrinters()) gTasks[taskId].tState = APPEALSTATE_UPDATE_OPPONENT_HEARTS; return; case APPEALSTATE_UPDATE_OPPONENT_HEARTS: for (i = 0; gTasks[taskId].data[1] != gContestantTurnOrder[i]; i++) ; UpdateAppealHearts(eContestantStatus[i].appeal + eContestantStatus[i].jam, -eContestantStatus[i].jam, i); gTasks[taskId].tState = APPEALSTATE_WAIT_OPPONENT_HEARTS; return; case APPEALSTATE_WAIT_OPPONENT_HEARTS: for (i = 0; gTasks[taskId].data[1] != gContestantTurnOrder[i]; i++) ; if (!eContestGfxState[i].updatingAppealHearts) gTasks[taskId].tState = APPEALSTATE_UPDATE_OPPONENT_STARS; return; case APPEALSTATE_UPDATE_OPPONENT_STARS: for (i = 0; gTasks[taskId].data[1] != gContestantTurnOrder[i]; i++) ; if (UpdateConditionStars(i, TRUE)) { gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_WAIT_OPPONENT_STARS; } else { gTasks[taskId].tState = APPEALSTATE_UPDATE_OPPONENT_STATUS; } return; case APPEALSTATE_WAIT_OPPONENT_STARS: if (++gTasks[taskId].tCounter > 20) { gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_UPDATE_OPPONENT_STATUS; } return; case APPEALSTATE_UPDATE_OPPONENT_STATUS: for (i = 0; i < CONTESTANT_COUNT; i++) { if (gContestantTurnOrder[i] == gTasks[taskId].data[1]) break; } if (DrawStatusSymbol(i)) PlaySE(SE_CONTEST_ICON_CHANGE); else PlaySE(SE_CONTEST_ICON_CLEAR); if (eContestantStatus[i].judgesAttentionWasRemoved) { StopFlashJudgeAttentionEye(i); eContestantStatus[i].judgesAttentionWasRemoved = FALSE; } gTasks[taskId].data[1]++; gTasks[taskId].tState = APPEALSTATE_UPDATE_OPPONENT; return; case APPEALSTATE_TRY_PRINT_SKIP_NEXT_TURN_MSG: if (gTasks[taskId].tCounter++ > 9) { gTasks[taskId].tCounter = 0; if (eContestantStatus[contestant].numTurnsSkipped != 0 || eContestantStatus[contestant].turnSkipped) { ContestClearGeneralTextWindow(); StringCopy(gStringVar1, gContestMons[contestant].nickname); StringExpandPlaceholders(gStringVar4, gText_MonCantAppealNextTurn); Contest_StartTextPrinter(gStringVar4, 1); } gTasks[taskId].tState = APPEALSTATE_WAIT_SKIP_NEXT_TURN_MSG; } return; case APPEALSTATE_WAIT_SKIP_NEXT_TURN_MSG: if (!Contest_RunTextPrinters()) { if (!eContestantStatus[contestant].usedComboMove) gTasks[taskId].tState = APPEALSTATE_CHECK_REPEATED_MOVE; else gTasks[taskId].tState = APPEALSTATE_PRINT_COMBO_MSG; } return; case APPEALSTATE_PRINT_COMBO_MSG: { s8 completedCombo = eContestantStatus[contestant].completedCombo; if (eContestantStatus[contestant].completedCombo) { // Finished combo // Looks like there were originally meant to be move combos of // different effectivenesses. completedCombo however is only ever 0 or 1, // so in here only "Went over well" will ever be used ContestClearGeneralTextWindow(); if (completedCombo == 1) Contest_StartTextPrinter(gText_AppealComboWentOverWell, TRUE); else if (completedCombo == 2) Contest_StartTextPrinter(gText_AppealComboWentOverVeryWell, TRUE); else Contest_StartTextPrinter(gText_AppealComboWentOverExcellently, TRUE); DoJudgeSpeechBubble(JUDGE_SYMBOL_TWO_EXCLAMATIONS); gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_WAIT_JUDGE_COMBO; } else { // Started combo ContestClearGeneralTextWindow(); StringCopy(gStringVar1, gContestMons[contestant].nickname); StringExpandPlaceholders(gStringVar4, gText_JudgeLookedAtMonExpectantly); Contest_StartTextPrinter(gStringVar4, 1); DoJudgeSpeechBubble(JUDGE_SYMBOL_ONE_EXCLAMATION); gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_WAIT_JUDGE_COMBO; } return; } case APPEALSTATE_WAIT_JUDGE_COMBO: if (!eContest.waitForJudgeSpeechBubble) { StartStopFlashJudgeAttentionEye(eContest.currentContestant); gTasks[taskId].tState = APPEALSTATE_TRY_UPDATE_HEARTS_FROM_COMBO; } return; case APPEALSTATE_TRY_UPDATE_HEARTS_FROM_COMBO: if (!Contest_RunTextPrinters()) { if (++gTasks[taskId].tCounter > 50) { if (!eContestantStatus[contestant].hasJudgesAttention) { UpdateAppealHearts( eContestantStatus[contestant].appeal, eContestantStatus[contestant].comboAppealBonus, contestant); eContestantStatus[contestant].appeal += eContestantStatus[contestant].comboAppealBonus; } gTasks[taskId].tState = APPEALSTATE_WAIT_HEARTS_FROM_COMBO; } } return; case APPEALSTATE_WAIT_HEARTS_FROM_COMBO: if (!eContestGfxState[contestant].updatingAppealHearts) { gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_CHECK_REPEATED_MOVE; } return; case APPEALSTATE_CHECK_REPEATED_MOVE: if (eContestantStatus[contestant].repeatedMove) { ContestClearGeneralTextWindow(); StringCopy(gStringVar1, gContestMons[contestant].nickname); StringExpandPlaceholders(gStringVar4, gText_RepeatedAppeal); Contest_StartTextPrinter(gStringVar4, TRUE); gTasks[taskId].tCounter = 0; DoJudgeSpeechBubble(JUDGE_SYMBOL_SWIRL); gTasks[taskId].tState = APPEALSTATE_WAIT_JUDGE_REPEATED_MOVE; } else { gTasks[taskId].tState = APPEALSTATE_UPDATE_CROWD; } return; case APPEALSTATE_WAIT_JUDGE_REPEATED_MOVE: if (!eContest.waitForJudgeSpeechBubble) gTasks[taskId].tState = APPEALSTATE_UPDATE_HEARTS_FROM_REPEAT; return; case APPEALSTATE_UPDATE_HEARTS_FROM_REPEAT: if (!Contest_RunTextPrinters()) { UpdateAppealHearts(eContestantStatus[contestant].appeal, -eContestantStatus[contestant].repeatJam, contestant); eContestantStatus[contestant].appeal -= eContestantStatus[contestant].repeatJam; gTasks[taskId].tState = APPEALSTATE_WAIT_HEARTS_FROM_REPEAT; } return; case APPEALSTATE_WAIT_HEARTS_FROM_REPEAT: ContestDebugDoPrint(); if (!eContestGfxState[contestant].updatingAppealHearts) { gTasks[taskId].tCounter = 0; ContestClearGeneralTextWindow(); gTasks[taskId].tState = APPEALSTATE_UPDATE_CROWD; } return; case APPEALSTATE_UPDATE_CROWD: if (eContestExcitement.frozen && contestant != eContestExcitement.freezer) { gTasks[taskId].tState = APPEALSTATE_PRINT_CROWD_WATCHES_MSG; } else { r3 = eContestExcitement.moveExcitement; // Can't get this to use local variable. Should be "moveExcitement" if (eContestantStatus[contestant].overrideCategoryExcitementMod) { r3 = 1; StringCopy(gStringVar3, gMoveNames[eContestantStatus[contestant].currMove]); } else { StringCopy(gStringVar3, sContestConditions[gContestMoves[eContestantStatus[contestant].currMove].contestCategory]); } if (r3 > 0 && eContestantStatus[contestant].repeatedMove) r3 = 0; ContestClearGeneralTextWindow(); StringCopy(gStringVar1, gContestMons[contestant].nickname); eContest.applauseLevel += r3; if (eContest.applauseLevel < 0) eContest.applauseLevel = 0; if (r3 == 0) { gTasks[taskId].tState = APPEALSTATE_SLIDE_APPLAUSE_OUT; } else { if (r3 < 0) StringExpandPlaceholders(gStringVar4, gText_MonsXDidntGoOverWell); else if (r3 > 0 && eContest.applauseLevel <= 4) StringExpandPlaceholders(gStringVar4, gText_MonsXWentOverGreat); else StringExpandPlaceholders(gStringVar4, gText_MonsXGotTheCrowdGoing); Contest_StartTextPrinter(gStringVar4, 1); gTasks[taskId].tCounter = 0; gTasks[taskId].data[11] = 0; if (r3 < 0) gTasks[taskId].tState = APPEALSTATE_DO_CROWD_UNEXCITED; else gTasks[taskId].tState = APPEALSTATE_DO_CROWD_EXCITED; } } return; case APPEALSTATE_DO_CROWD_UNEXCITED: switch (gTasks[taskId].tCounter) { case 0: BlendAudienceBackground(-1, 1); PlayFanfare(MUS_TOO_BAD); gTasks[taskId].tCounter++; break; case 1: if (!eContest.waitForAudienceBlend && !Contest_RunTextPrinters()) { ShowAndUpdateApplauseMeter(-1); gTasks[taskId].tCounter++; } break; case 2: if (!eContest.isShowingApplauseMeter) { if (gTasks[taskId].data[11]++ > 29) { gTasks[taskId].data[11] = 0; BlendAudienceBackground(-1, -1); gTasks[taskId].tCounter++; } } break; case 3: if (!gPaletteFade.active) { gTasks[taskId].tCounter = 0; gTasks[taskId].data[11] = 0; gTasks[taskId].tState = APPEALSTATE_WAIT_EXCITEMENT_HEARTS; } break; } return; case APPEALSTATE_DO_CROWD_EXCITED: switch (gTasks[taskId].tCounter) { case 0: if (!Contest_RunTextPrinters()) { BlendAudienceBackground(1, 1); gTasks[taskId].tCounter++; } break; case 1: if (!eContest.waitForAudienceBlend) { AnimateAudience(); PlaySE(SE_M_ENCORE2); ShowAndUpdateApplauseMeter(1); gTasks[taskId].tCounter++; } break; case 2: if (!eContest.isShowingApplauseMeter) { if (gTasks[taskId].data[11]++ > 29) { gTasks[taskId].data[11] = 0; UpdateAppealHearts(eContestantStatus[contestant].appeal, eContestExcitement.excitementAppealBonus, contestant); eContestantStatus[contestant].appeal += eContestExcitement.excitementAppealBonus; gTasks[taskId].tCounter++; } } break; case 3: if (!eContestGfxState[contestant].updatingAppealHearts) { if (!eContest.animatingAudience) { BlendAudienceBackground(1, -1); gTasks[taskId].tCounter++; } } break; case 4: if (!gPaletteFade.active) { gTasks[taskId].tCounter = 0; gTasks[taskId].data[11] = 0; gTasks[taskId].tState = APPEALSTATE_WAIT_EXCITEMENT_HEARTS; } break; } return; case APPEALSTATE_WAIT_EXCITEMENT_HEARTS: if (!eContestGfxState[contestant].updatingAppealHearts) { ContestClearGeneralTextWindow(); gTasks[taskId].tState = APPEALSTATE_SLIDE_APPLAUSE_OUT; } return; case APPEALSTATE_PRINT_CROWD_WATCHES_MSG: ContestClearGeneralTextWindow(); StringCopy(gStringVar3, gContestMons[eContestExcitement.freezer].nickname); StringCopy(gStringVar1, gContestMons[contestant].nickname); StringCopy(gStringVar2, gMoveNames[eContestantStatus[contestant].currMove]); StringExpandPlaceholders(gStringVar4, gText_CrowdContinuesToWatchMon); Contest_StartTextPrinter(gStringVar4, TRUE); gTasks[taskId].tState = APPEALSTATE_PRINT_MON_MOVE_IGNORED_MSG; return; case APPEALSTATE_PRINT_MON_MOVE_IGNORED_MSG: if (!Contest_RunTextPrinters()) { ContestClearGeneralTextWindow(); StringExpandPlaceholders(gStringVar4, gText_MonsMoveIsIgnored); Contest_StartTextPrinter(gStringVar4, TRUE); gTasks[taskId].tState = APPEALSTATE_WAIT_MON_MOVE_IGNORED_MSG; } return; case APPEALSTATE_WAIT_MON_MOVE_IGNORED_MSG: if (!Contest_RunTextPrinters()) { ContestClearGeneralTextWindow(); gTasks[taskId].tState = APPEALSTATE_SLIDE_APPLAUSE_OUT; } return; case APPEALSTATE_PRINT_TOO_NERVOUS_MSG: if (eContestantStatus[contestant].hasJudgesAttention) eContestantStatus[contestant].hasJudgesAttention = FALSE; StartStopFlashJudgeAttentionEye(contestant); StringCopy(gStringVar1, gContestMons[contestant].nickname); StringCopy(gStringVar2, gMoveNames[eContestantStatus[contestant].currMove]); StringExpandPlaceholders(gStringVar4, gText_MonWasTooNervousToMove); Contest_StartTextPrinter(gStringVar4, TRUE); gTasks[taskId].tState = APPEALSTATE_WAIT_TOO_NERVOUS_MSG; return; case APPEALSTATE_WAIT_TOO_NERVOUS_MSG: if (!Contest_RunTextPrinters()) gTasks[taskId].tState = APPEALSTATE_SLIDE_APPLAUSE_OUT; return; case APPEALSTATE_SLIDE_APPLAUSE_OUT: SlideApplauseMeterOut(); gTasks[taskId].tState = APPEALSTATE_WAIT_SLIDE_APPLAUSE; return; case APPEALSTATE_WAIT_SLIDE_APPLAUSE: if (!eContest.applauseMeterIsMoving) { if (eContest.applauseLevel > 4) { eContest.applauseLevel = 0; UpdateApplauseMeter(); } gTasks[taskId].tState = APPEALSTATE_SLIDE_MON_OUT; } return; case APPEALSTATE_SLIDE_MON_OUT: spriteId = gTasks[taskId].tMonSpriteId; gSprites[spriteId].callback = SpriteCB_MonSlideOut; gTasks[taskId].tState = APPEALSTATE_FREE_MON_SPRITE; return; case APPEALSTATE_FREE_MON_SPRITE: spriteId = gTasks[taskId].tMonSpriteId; if (gSprites[spriteId].invisible) { FreeSpriteOamMatrix(&gSprites[spriteId]); DestroySprite(&gSprites[spriteId]); gTasks[taskId].tState = APPEALSTATE_START_TURN_END_DELAY; } return; case APPEALSTATE_START_TURN_END_DELAY: gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_TURN_END_DELAY; return; case APPEALSTATE_PRINT_SKIP_TURN_MSG: ContestClearGeneralTextWindow(); StringCopy(gStringVar1, gContestMons[contestant].nickname); StringExpandPlaceholders(gStringVar4, gText_MonWasWatchingOthers); Contest_StartTextPrinter(gStringVar4, TRUE); gTasks[taskId].tState = APPEALSTATE_WAIT_SKIP_TURN_MSG; return; case APPEALSTATE_WAIT_SKIP_TURN_MSG: if (!Contest_RunTextPrinters()) gTasks[taskId].tState = APPEALSTATE_TURN_END_DELAY; return; case APPEALSTATE_TURN_END_DELAY: if (++gTasks[taskId].tCounter > 29) { gTasks[taskId].tCounter = 0; gTasks[taskId].tState = APPEALSTATE_START_NEXT_TURN; } return; case APPEALSTATE_START_NEXT_TURN: if (++eContest.turnNumber == CONTESTANT_COUNT) { gTasks[taskId].tState = 0; gTasks[taskId].data[1] = 0; gTasks[taskId].tMonSpriteId = 0; gTasks[taskId].func = Task_FinishRoundOfAppeals; } else { gTasks[taskId].tState = APPEALSTATE_START_TURN; } return; } } static void Task_EndWaitForLink(u8 taskId) { eContest.waitForLink = FALSE; DestroyTask(taskId); } static void SpriteCB_MonSlideIn(struct Sprite *sprite) { if (sprite->x2 != 0) { sprite->x2 -= 2; } else { if (++sprite->data[0] == 31) { sprite->data[0] = 0; sprite->callback = SpriteCallbackDummy; } } } static void SpriteCB_MonSlideOut(struct Sprite *sprite) { sprite->x2 -= 6; if (sprite->x + sprite->x2 < -32) { sprite->callback = SpriteCallbackDummy; sprite->invisible = TRUE; } } static void Task_FinishRoundOfAppeals(u8 taskId) { switch (gTasks[taskId].data[0]) { case 0: if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) { u8 taskId2; eContest.waitForLink = TRUE; if (IsPlayerLinkLeader()) { RankContestants(); SetAttentionLevels(); } taskId2 = CreateTask(Task_LinkContest_CommunicateAppealsState, 0); SetTaskFuncWithFollowupFunc(taskId2, Task_LinkContest_CommunicateAppealsState, Task_EndWaitForLink); ContestPrintLinkStandby(); gTasks[taskId].data[0] = 1; } else { RankContestants(); SetAttentionLevels(); gTasks[taskId].data[0] = 2; } break; case 1: if (!eContest.waitForLink) gTasks[taskId].data[0] = 2; break; case 2: gTasks[taskId].data[0] = 0; gTasks[taskId].func = Task_ReadyUpdateHeartSliders; break; } } static void Task_ReadyUpdateHeartSliders(u8 taskId) { ShowHideNextTurnGfx(FALSE); gTasks[taskId].data[0] = 0; gTasks[taskId].data[1] = 0; gTasks[taskId].func = Task_UpdateHeartSliders; } static void Task_UpdateHeartSliders(u8 taskId) { switch (gTasks[taskId].data[0]) { case 0: if (++gTasks[taskId].data[1] > 20) { AnimateSliderHearts(SLIDER_HEART_ANIM_APPEAR); gTasks[taskId].data[1] = 0; gTasks[taskId].data[0]++; } break; case 1: if (!eContest.sliderHeartsAnimating) { if (++gTasks[taskId].data[1] > 20) { gTasks[taskId].data[1] = 0; gTasks[taskId].data[0]++; } } break; case 2: UpdateHeartSliders(); gTasks[taskId].data[0] = 0; gTasks[taskId].data[1] = 0; gTasks[taskId].func = Task_WaitForHeartSliders; break; } } static void Task_WaitForHeartSliders(u8 taskId) { if (SlidersDoneUpdating()) gTasks[taskId].func = Task_RestorePlttBufferUnfaded; } static void Task_RestorePlttBufferUnfaded(u8 taskId) { DmaCopy32Defvars(3, eContestTempSave.cachedPlttBufferUnfaded, gPlttBufferUnfaded, PLTT_BUFFER_SIZE * 2); gTasks[taskId].data[0] = 0; gTasks[taskId].data[1] = 2; gTasks[taskId].func = Task_WaitPrintRoundResult; } static void Task_WaitPrintRoundResult(u8 taskId) { if (++gTasks[taskId].data[0] > 2) { gTasks[taskId].data[0] = 0; if (--gTasks[taskId].data[1] == 0) gTasks[taskId].func = Task_PrintRoundResultText; } } static void Task_PrintRoundResultText(u8 taskId) { if (gTasks[taskId].data[0] == 0) { u8 attention = eContestantStatus[gContestPlayerMonIndex].attentionLevel; ContestClearGeneralTextWindow(); StringCopy(gStringVar1, gContestMons[gContestPlayerMonIndex].nickname); StringExpandPlaceholders(gStringVar4, sRoundResultTexts[attention]); Contest_StartTextPrinter(gStringVar4, TRUE); gTasks[taskId].data[0]++; } else { if (!Contest_RunTextPrinters()) { gTasks[taskId].data[0] = 0; gTasks[taskId].func = Task_ReUpdateHeartSliders; ContestDebugDoPrint(); } } } static void Task_ReUpdateHeartSliders(u8 taskId) { if (gTasks[taskId].data[0]++ > 29) { gTasks[taskId].data[0] = 0; UpdateHeartSliders(); // ? Sliders have already been updated gTasks[taskId].func = Task_WaitForHeartSlidersAgain; } } static void Task_WaitForHeartSlidersAgain(u8 taskId) { if (SlidersDoneUpdating()) { gTasks[taskId].data[0] = 0; gTasks[taskId].func = Task_DropCurtainAtRoundEnd; } } static void Task_DropCurtainAtRoundEnd(u8 taskId) { SetBgForCurtainDrop(); gTasks[taskId].func = Task_StartDropCurtainAtRoundEnd; } static void Task_UpdateContestantBoxOrder(u8 taskId) { UpdateContestantBoxOrder(); gTasks[taskId].func = Task_TryStartNextRoundOfAppeals; } static void Task_TryStartNextRoundOfAppeals(u8 taskId) { vu16 sp0 = GetGpuReg(REG_OFFSET_BG0CNT); vu16 sp2 = GetGpuReg(REG_OFFSET_BG2CNT); ((vBgCnt *)&sp0)->priority = 0; ((vBgCnt *)&sp2)->priority = 0; SetGpuReg(REG_OFFSET_BG0CNT, sp0); SetGpuReg(REG_OFFSET_BG2CNT, sp2); eContest.appealNumber++; if (eContest.appealNumber == CONTEST_NUM_APPEALS) { gTasks[taskId].func = Task_EndAppeals; } else { SlideApplauseMeterIn(); gTasks[taskId].func = Task_StartNewRoundOfAppeals; } } static void Task_StartNewRoundOfAppeals(u8 taskId) { if (!eContest.applauseMeterIsMoving) gTasks[taskId].func = Task_DisplayAppealNumberText; } static void Task_EndAppeals(u8 taskId) { s32 i; gBattle_BG0_Y = 0; gBattle_BG2_Y = 0; for (i = 0; i < CONTESTANT_COUNT; i++) gContestMonAppealPointTotals[i] = eContestantStatus[i].pointTotal; CalculateFinalScores(); ContestClearGeneralTextWindow(); if (!(gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK)) BravoTrainerPokemonProfile_BeforeInterview1(eContestantStatus[gContestPlayerMonIndex].prevMove); else { CalculateContestLiveUpdateData(); SetConestLiveUpdateTVData(); ContestDebugPrintBitStrings(); } gContestRngValue = gRngValue; StringExpandPlaceholders(gStringVar4, gText_AllOutOfAppealTime); Contest_StartTextPrinter(gStringVar4, TRUE); gTasks[taskId].data[2] = 0; gTasks[taskId].func = Task_WaitForOutOfTimeMsg; } static void Task_WaitForOutOfTimeMsg(u8 taskId) { if (!Contest_RunTextPrinters()) { SetBgForCurtainDrop(); gBattle_BG1_X = 0; gBattle_BG1_Y = DISPLAY_HEIGHT; PlaySE12WithPanning(SE_CONTEST_CURTAIN_FALL, 0); gTasks[taskId].data[0] = 0; gTasks[taskId].func = Task_DropCurtainAtAppealsEnd; } } static void Task_DropCurtainAtAppealsEnd(u8 taskId) { gBattle_BG1_Y -= 7; if ((s16)gBattle_BG1_Y < 0) gBattle_BG1_Y = 0; if (gBattle_BG1_Y == 0) { gTasks[taskId].func = Task_TryCommunicateFinalStandings; gTasks[taskId].data[0] = 0; } } static void Task_TryCommunicateFinalStandings(u8 taskId) { if (gTasks[taskId].data[0]++ >= 50) { gTasks[taskId].data[0] = 0; if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) { gTasks[taskId].func = Task_CommunicateFinalStandings; } else { BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); gTasks[taskId].func = Task_ContestReturnToField; } } } static void Task_CommunicateFinalStandings(u8 taskId) { u8 taskId2 = CreateTask(Task_LinkContest_CommunicateFinalStandings, 0); SetTaskFuncWithFollowupFunc(taskId2, Task_LinkContest_CommunicateFinalStandings, Task_EndCommunicateFinalStandings); gTasks[taskId].func = TaskDummy1; ContestPrintLinkStandby(); SetBottomSliderHeartsInvisibility(FALSE); } static void Task_EndCommunicateFinalStandings(u8 taskId) { DestroyTask(taskId); BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); gTasks[eContest.mainTaskId].func = Task_ContestReturnToField; } static void Task_ContestReturnToField(u8 taskId) { if (!gPaletteFade.active) { DestroyTask(taskId); gFieldCallback = FieldCB_ContestReturnToField; FreeAllWindowBuffers(); FreeContestResources(); FreeMonSpritesGfx(); SetMainCallback2(CB2_ReturnToField); } } static void FieldCB_ContestReturnToField(void) { ScriptContext2_Disable(); EnableBothScriptContexts(); } static void TryPutPlayerLast(void) { if (!(gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK)) gContestPlayerMonIndex = CONTESTANT_COUNT - 1; } static bool8 IsPlayerLinkLeader(void) { if (gContestPlayerMonIndex == gContestLinkLeaderIndex) return TRUE; return FALSE; } void CreateContestMonFromParty(u8 partyIndex) { u8 name[20]; u16 heldItem; s16 cool; s16 beauty; s16 cute; s16 smart; s16 tough; StringCopy(name, gSaveBlock2Ptr->playerName); if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) { StripPlayerNameForLinkContest(name); } memcpy(gContestMons[gContestPlayerMonIndex].trainerName, name, 8); if (gSaveBlock2Ptr->playerGender == MALE) gContestMons[gContestPlayerMonIndex].trainerGfxId = OBJ_EVENT_GFX_LINK_BRENDAN; else gContestMons[gContestPlayerMonIndex].trainerGfxId = OBJ_EVENT_GFX_LINK_MAY; gContestMons[gContestPlayerMonIndex].aiFlags = 0; gContestMons[gContestPlayerMonIndex].highestRank = 0; gContestMons[gContestPlayerMonIndex].species = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPECIES); GetMonData(&gPlayerParty[partyIndex], MON_DATA_NICKNAME, name); StringGet_Nickname(name); if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) { StripMonNameForLinkContest(name, GetMonData(&gPlayerParty[partyIndex], MON_DATA_LANGUAGE)); } memcpy(gContestMons[gContestPlayerMonIndex].nickname, name, POKEMON_NAME_LENGTH + 1); StringCopy(gContestMons[gContestPlayerMonIndex].nickname, name); gContestMons[gContestPlayerMonIndex].cool = GetMonData(&gPlayerParty[partyIndex], MON_DATA_COOL); gContestMons[gContestPlayerMonIndex].beauty = GetMonData(&gPlayerParty[partyIndex], MON_DATA_BEAUTY); gContestMons[gContestPlayerMonIndex].cute = GetMonData(&gPlayerParty[partyIndex], MON_DATA_CUTE); gContestMons[gContestPlayerMonIndex].smart = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SMART); gContestMons[gContestPlayerMonIndex].tough = GetMonData(&gPlayerParty[partyIndex], MON_DATA_TOUGH); gContestMons[gContestPlayerMonIndex].sheen = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SHEEN); gContestMons[gContestPlayerMonIndex].moves[0] = GetMonData(&gPlayerParty[partyIndex], MON_DATA_MOVE1); gContestMons[gContestPlayerMonIndex].moves[1] = GetMonData(&gPlayerParty[partyIndex], MON_DATA_MOVE2); gContestMons[gContestPlayerMonIndex].moves[2] = GetMonData(&gPlayerParty[partyIndex], MON_DATA_MOVE3); gContestMons[gContestPlayerMonIndex].moves[3] = GetMonData(&gPlayerParty[partyIndex], MON_DATA_MOVE4); gContestMons[gContestPlayerMonIndex].personality = GetMonData(&gPlayerParty[partyIndex], MON_DATA_PERSONALITY); gContestMons[gContestPlayerMonIndex].otId = GetMonData(&gPlayerParty[partyIndex], MON_DATA_OT_ID); heldItem = GetMonData(&gPlayerParty[partyIndex], MON_DATA_HELD_ITEM); cool = gContestMons[gContestPlayerMonIndex].cool; beauty = gContestMons[gContestPlayerMonIndex].beauty; cute = gContestMons[gContestPlayerMonIndex].cute; smart = gContestMons[gContestPlayerMonIndex].smart; tough = gContestMons[gContestPlayerMonIndex].tough; if (heldItem == ITEM_RED_SCARF) cool += 20; else if (heldItem == ITEM_BLUE_SCARF) beauty += 20; else if (heldItem == ITEM_PINK_SCARF) cute += 20; else if (heldItem == ITEM_GREEN_SCARF) smart += 20; else if (heldItem == ITEM_YELLOW_SCARF) tough += 20; if (cool > 255) cool = 255; if (beauty > 255) beauty = 255; if (cute > 255) cute = 255; if (smart > 255) smart = 255; if (tough > 255) tough = 255; gContestMons[gContestPlayerMonIndex].cool = cool; gContestMons[gContestPlayerMonIndex].beauty = beauty; gContestMons[gContestPlayerMonIndex].cute = cute; gContestMons[gContestPlayerMonIndex].smart = smart; gContestMons[gContestPlayerMonIndex].tough = tough; } void SetContestants(u8 contestType, u8 rank) { s32 i; u8 opponentsCount = 0; u8 opponents[100]; bool8 allowPostgameContestants = FALSE; const u8 * filter; TryPutPlayerLast(); if (FlagGet(FLAG_SYS_GAME_CLEAR) && !(gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK)) allowPostgameContestants = TRUE; // Find all suitable opponents filter = gPostgameContestOpponentFilter; for (i = 0; i < ARRAY_COUNT(gContestOpponents); i++) { if (rank == gContestOpponents[i].whichRank) { if (allowPostgameContestants == TRUE) { if (filter[i] == CONTEST_FILTER_NO_POSTGAME) continue; } else { if (filter[i] == CONTEST_FILTER_ONLY_POSTGAME) continue; } if (contestType == CONTEST_CATEGORY_COOL && gContestOpponents[i].aiPool_Cool) opponents[opponentsCount++] = i; else if (contestType == CONTEST_CATEGORY_BEAUTY && gContestOpponents[i].aiPool_Beauty) opponents[opponentsCount++] = i; else if (contestType == CONTEST_CATEGORY_CUTE && gContestOpponents[i].aiPool_Cute) opponents[opponentsCount++] = i; else if (contestType == CONTEST_CATEGORY_SMART && gContestOpponents[i].aiPool_Smart) opponents[opponentsCount++] = i; else if (contestType == CONTEST_CATEGORY_TOUGH && gContestOpponents[i].aiPool_Tough) opponents[opponentsCount++] = i; } } opponents[opponentsCount] = 0xFF; // Choose three random opponents from the list for (i = 0; i < CONTESTANT_COUNT - 1; i++) { u16 rnd = Random() % opponentsCount; s32 j; gContestMons[i] = gContestOpponents[opponents[rnd]]; for (j = rnd; opponents[j] != 0xFF; j++) opponents[j] = opponents[j + 1]; opponentsCount--; } CreateContestMonFromParty(gContestMonPartyIndex); } void SetLinkAIContestants(u8 contestType, u8 rank, bool32 isPostgame) { s32 i, j; u8 opponentsCount = 0; u8 opponents[100]; if (gNumLinkContestPlayers == CONTESTANT_COUNT) return; // Find all suitable AI opponents for (i = 0; i < ARRAY_COUNT(gContestOpponents); i++) { if (rank != gContestOpponents[i].whichRank) continue; if (isPostgame == TRUE) { if (gPostgameContestOpponentFilter[i] == CONTEST_FILTER_NO_POSTGAME) continue; } else { if (gPostgameContestOpponentFilter[i] == CONTEST_FILTER_ONLY_POSTGAME) continue; } if ((contestType == CONTEST_CATEGORY_COOL && gContestOpponents[i].aiPool_Cool) || (contestType == CONTEST_CATEGORY_BEAUTY && gContestOpponents[i].aiPool_Beauty) || (contestType == CONTEST_CATEGORY_CUTE && gContestOpponents[i].aiPool_Cute) || (contestType == CONTEST_CATEGORY_SMART && gContestOpponents[i].aiPool_Smart) || (contestType == CONTEST_CATEGORY_TOUGH && gContestOpponents[i].aiPool_Tough)) opponents[opponentsCount++] = i; } opponents[opponentsCount] = 0xFF; // Fill remaining contestant slots with random AI opponents from the list for (i = 0; i < CONTESTANT_COUNT - gNumLinkContestPlayers; i++) { u16 rnd = GetContestRand() % opponentsCount; gContestMons[gNumLinkContestPlayers + i] = gContestOpponents[opponents[rnd]]; StripPlayerNameForLinkContest(gContestMons[gNumLinkContestPlayers + i].trainerName); StripMonNameForLinkContest(gContestMons[gNumLinkContestPlayers + i].nickname, GAME_LANGUAGE); for (j = rnd; opponents[j] != 0xFF; j++) opponents[j] = opponents[j + 1]; opponentsCount--; } } u8 GetContestEntryEligibility(struct Pokemon *pkmn) { u8 ribbon; u8 eligibility; if (GetMonData(pkmn, MON_DATA_IS_EGG)) return CANT_ENTER_CONTEST_EGG; if (GetMonData(pkmn, MON_DATA_HP) == 0) return CANT_ENTER_CONTEST_FAINTED; switch (gSpecialVar_ContestCategory) { case CONTEST_CATEGORY_COOL: ribbon = GetMonData(pkmn, MON_DATA_COOL_RIBBON); break; case CONTEST_CATEGORY_BEAUTY: ribbon = GetMonData(pkmn, MON_DATA_BEAUTY_RIBBON); break; case CONTEST_CATEGORY_CUTE: ribbon = GetMonData(pkmn, MON_DATA_CUTE_RIBBON); break; case CONTEST_CATEGORY_SMART: ribbon = GetMonData(pkmn, MON_DATA_SMART_RIBBON); break; case CONTEST_CATEGORY_TOUGH: ribbon = GetMonData(pkmn, MON_DATA_TOUGH_RIBBON); break; default: return CANT_ENTER_CONTEST; } // Couldn't get this to match any other way. // Returns 2, 1, or 0 respectively if ribbon's rank is above, equal, or below // the current contest rank. if (ribbon > gSpecialVar_ContestRank) eligibility = CAN_ENTER_CONTEST_HIGH_RANK; else if (ribbon >= gSpecialVar_ContestRank) eligibility = CAN_ENTER_CONTEST_EQUAL_RANK; else eligibility = CANT_ENTER_CONTEST; return eligibility; } static void DrawContestantWindowText(void) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) { FillWindowPixelBuffer(gContestantTurnOrder[i], PIXEL_FILL(0)); PrintContestantTrainerName(i); PrintContestantMonName(i); } } static u8 *Contest_CopyStringWithColor(const u8 *string, u8 color) { u8 * ptr = StringCopy(gDisplayedStringBattle, gText_ColorTransparent); ptr[-1] = color; // Overwrites the "{COLOR TRANSPARENT}" part of the string. ptr = StringCopy(ptr, string); return ptr; } static void PrintContestantTrainerName(u8 contestant) { PrintContestantTrainerNameWithColor(contestant, contestant + CONTESTANT_TEXT_COLOR_START); } static void PrintContestantTrainerNameWithColor(u8 contestant, u8 color) { u8 buffer[32]; s32 offset; StringCopy(buffer, gText_Slash); StringAppend(buffer, gContestMons[contestant].trainerName); Contest_CopyStringWithColor(buffer, color); offset = GetStringRightAlignXOffset(FONT_NARROW, gDisplayedStringBattle, 0x60); if (offset > 55) offset = 55; Contest_PrintTextToBg0WindowAt(gContestantTurnOrder[contestant], gDisplayedStringBattle, offset, 1, FONT_NARROW); } static void PrintContestantMonName(u8 contestant) { PrintContestantMonNameWithColor(contestant, contestant + CONTESTANT_TEXT_COLOR_START); } static void PrintContestantMonNameWithColor(u8 contestant, u8 color) { Contest_CopyStringWithColor(gContestMons[contestant].nickname, color); Contest_PrintTextToBg0WindowAt(gContestantTurnOrder[contestant], gDisplayedStringBattle, 5, 1, FONT_NARROW); } static u16 CalculateContestantRound1Points(u8 who, u8 contestCategory) { u8 statMain; u8 statSub1; u8 statSub2; switch (contestCategory) { case CONTEST_CATEGORY_COOL: statMain = gContestMons[who].cool; statSub1 = gContestMons[who].tough; statSub2 = gContestMons[who].beauty; break; case CONTEST_CATEGORY_BEAUTY: statMain = gContestMons[who].beauty; statSub1 = gContestMons[who].cool; statSub2 = gContestMons[who].cute; break; case CONTEST_CATEGORY_CUTE: statMain = gContestMons[who].cute; statSub1 = gContestMons[who].beauty; statSub2 = gContestMons[who].smart; break; case CONTEST_CATEGORY_SMART: statMain = gContestMons[who].smart; statSub1 = gContestMons[who].cute; statSub2 = gContestMons[who].tough; break; case CONTEST_CATEGORY_TOUGH: default: statMain = gContestMons[who].tough; statSub1 = gContestMons[who].smart; statSub2 = gContestMons[who].cool; break; } return statMain + (statSub1 + statSub2 + gContestMons[who].sheen) / 2; } void CalculateRound1Points(u8 contestCategory) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) gContestMonRound1Points[i] = CalculateContestantRound1Points(i, contestCategory); } static u8 CreateJudgeSprite(void) { u8 spriteId; LoadCompressedSpriteSheet(&sSpriteSheet_Judge); LoadCompressedPalette(gContest2Pal, 0x110, 32); spriteId = CreateSprite(&sSpriteTemplate_Judge, 112, 36, 30); gSprites[spriteId].oam.paletteNum = 1; gSprites[spriteId].callback = SpriteCallbackDummy; return spriteId; } static u8 CreateJudgeSpeechBubbleSprite(void) { u8 spriteId; LoadCompressedSpriteSheet(&sSpriteSheet_JudgeSymbols); LoadCompressedSpritePalette(&sSpritePalette_JudgeSymbols); spriteId = CreateSprite(&sSpriteTemplate_JudgeSpeechBubble, 96, 10, 29); gSprites[spriteId].invisible = TRUE; gSprites[spriteId].data[0] = gSprites[spriteId].oam.tileNum; return spriteId; } static u8 CreateContestantSprite(u16 species, u32 otId, u32 personality, u32 index) { u8 spriteId; species = SanitizeSpecies(species); if (index == gContestPlayerMonIndex) HandleLoadSpecialPokePic_2(&gMonBackPicTable[species], gMonSpritesGfxPtr->sprites.ptr[B_POSITION_PLAYER_LEFT], species, personality); else HandleLoadSpecialPokePic_DontHandleDeoxys(&gMonBackPicTable[species], gMonSpritesGfxPtr->sprites.ptr[B_POSITION_PLAYER_LEFT], species, personality); LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality), 0x120, 0x20); SetMultiuseSpriteTemplateToPokemon(species, B_POSITION_PLAYER_LEFT); spriteId = CreateSprite(&gMultiuseSpriteTemplate, 0x70, GetBattlerSpriteFinal_Y(2, species, FALSE), 30); gSprites[spriteId].oam.paletteNum = 2; gSprites[spriteId].oam.priority = 2; gSprites[spriteId].subpriority = GetBattlerSpriteSubpriority(2); gSprites[spriteId].callback = SpriteCallbackDummy; gSprites[spriteId].data[0] = gSprites[spriteId].oam.paletteNum; gSprites[spriteId].data[2] = species; if (IsSpeciesNotUnown(species)) gSprites[spriteId].affineAnims = gAffineAnims_BattleSpriteContest; else gSprites[spriteId].affineAnims = gAffineAnims_BattleSpriteOpponentSide; StartSpriteAffineAnim(&gSprites[spriteId], BATTLER_AFFINE_NORMAL); return spriteId; } bool8 IsSpeciesNotUnown(u16 species) { if (species == SPECIES_UNOWN) return FALSE; else return TRUE; } // The contestant info windows and general-purpose text box are drawn on one half, while // the moves and move description windows are drawn on another screen. Only the first 32 * 20 // tiles are actually drawn on screen. static void SwapMoveDescAndContestTilemaps(void) { CpuCopy16(gContestResources->contestBgTilemaps[0], gContestResources->contestBgTilemaps[0] + 0x500, 32 * 20); CpuCopy16(gContestResources->contestBgTilemaps[2], gContestResources->contestBgTilemaps[2] + 0x500, 32 * 20); } // Functionally unused static u16 GetMoveEffectSymbolTileOffset(u16 move, u8 contestant) { u16 offset; switch (gContestEffects[gContestMoves[move].effect].effectType) { case 0: case 1: case 8: offset = 0x9082; break; case 2: case 3: offset = 0x9088; break; default: offset = 0x9086; break; } offset += 0x9000 + (contestant << 12); return offset; } static void PrintContestMoveDescription(u16 a) { u8 category; u16 categoryTile; u8 numHearts; // The contest category icon is implemented as a 5x2 group of tiles. category = gContestMoves[a].contestCategory; if (category == CONTEST_CATEGORY_COOL) categoryTile = 0x4040; else if (category == CONTEST_CATEGORY_BEAUTY) categoryTile = 0x4045; else if (category == CONTEST_CATEGORY_CUTE) categoryTile = 0x404A; else if (category == CONTEST_CATEGORY_SMART) categoryTile = 0x406A; else categoryTile = 0x408A; ContestBG_FillBoxWithIncrementingTile(0, categoryTile, 0x0b, 0x1f, 0x05, 0x01, 0x11, 0x01); ContestBG_FillBoxWithIncrementingTile(0, categoryTile + 0x10, 0x0b, 0x20, 0x05, 0x01, 0x11, 0x01); // Appeal hearts if (gContestEffects[gContestMoves[a].effect].appeal == 0xFF) numHearts = 0; else numHearts = gContestEffects[gContestMoves[a].effect].appeal / 10; if (numHearts > MAX_CONTEST_MOVE_HEARTS) numHearts = MAX_CONTEST_MOVE_HEARTS; ContestBG_FillBoxWithTile(0, TILE_EMPTY_APPEAL_HEART, 0x15, 0x1f, MAX_CONTEST_MOVE_HEARTS, 0x01, 0x11); ContestBG_FillBoxWithTile(0, TILE_FILLED_APPEAL_HEART, 0x15, 0x1f, numHearts, 0x01, 0x11); // Jam hearts if (gContestEffects[gContestMoves[a].effect].jam == 0xFF) numHearts = 0; else numHearts = gContestEffects[gContestMoves[a].effect].jam / 10; if (numHearts > MAX_CONTEST_MOVE_HEARTS) numHearts = MAX_CONTEST_MOVE_HEARTS; ContestBG_FillBoxWithTile(0, TILE_EMPTY_JAM_HEART, 0x15, 0x20, MAX_CONTEST_MOVE_HEARTS, 0x01, 0x11); ContestBG_FillBoxWithTile(0, TILE_FILLED_JAM_HEART, 0x15, 0x20, numHearts, 0x01, 0x11); FillWindowPixelBuffer(WIN_MOVE_DESCRIPTION, PIXEL_FILL(0)); Contest_PrintTextToBg0WindowStd(WIN_MOVE_DESCRIPTION, gContestEffectDescriptionPointers[gContestMoves[a].effect]); Contest_PrintTextToBg0WindowStd(WIN_SLASH, gText_Slash); } static void DrawMoveEffectSymbol(u16 move, u8 contestant) { u8 contestantOffset = gContestantTurnOrder[contestant] * 5 + 2; if (!Contest_IsMonsTurnDisabled(contestant) && move != MOVE_NONE) { u16 tile = GetMoveEffectSymbolTileOffset(move, contestant); ContestBG_FillBoxWithIncrementingTile(0, tile, 20, contestantOffset, 2, 1, 17, 1); ContestBG_FillBoxWithIncrementingTile(0, tile + 16, 20, contestantOffset + 1, 2, 1, 17, 1); } else { ContestBG_FillBoxWithTile(0, 0, 20, contestantOffset, 2, 2, 17); } } // Unused static void DrawMoveEffectSymbols(void) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) DrawMoveEffectSymbol(eContestantStatus[i].currMove, i); } static u16 GetStarTileOffset(void) { return 0x2034; } static bool8 UpdateConditionStars(u8 contestantIdx, bool8 resetMod) { u8 contestantOffset; s32 numStars; if (eContestantStatus[contestantIdx].conditionMod == CONDITION_NO_CHANGE) return FALSE; contestantOffset = gContestantTurnOrder[contestantIdx] * 5 + 2; numStars = eContestantStatus[contestantIdx].condition / 10; if (eContestantStatus[contestantIdx].conditionMod == CONDITION_GAIN) { ContestBG_FillBoxWithTile(0, GetStarTileOffset(), 19, contestantOffset, 1, numStars, 17); if (resetMod) { PlaySE(SE_EXP_MAX); eContestantStatus[contestantIdx].conditionMod = CONDITION_NO_CHANGE; } } else // CONDITION_LOSE { ContestBG_FillBoxWithTile(0, 0, 19, contestantOffset + numStars, 1, 3 - numStars, 17); if (resetMod) { PlaySE(SE_CONTEST_CONDITION_LOSE); eContestantStatus[contestantIdx].conditionMod = CONDITION_NO_CHANGE; } } return TRUE; } static void DrawConditionStars(void) { s32 i; s32 numStars; for (i = 0; i < CONTESTANT_COUNT; i++) { u8 contestantOffset = gContestantTurnOrder[i] * 5 + 2; u16 starOffset = GetStarTileOffset(); numStars = eContestantStatus[i].condition / 10; ContestBG_FillBoxWithTile(0, starOffset, 19, contestantOffset, 1, numStars, 17); ContestBG_FillBoxWithTile(0, 0, 19, contestantOffset + numStars, 1, 3 - numStars, 17); } } static u16 GetStatusSymbolTileOffset(u8 status) { u16 offset = 0; switch (status) { case STAT_SYMBOL_CIRCLE: // For resistant offset = 0x80; break; case STAT_SYMBOL_WAVE: // For nervous offset = 0x84; break; case STAT_SYMBOL_X: // For turn skipped offset = 0x86; break; case STAT_SYMBOL_SWIRL: // For jammed/unnerved offset = 0x88; break; case STAT_SYMBOL_SQUARE: // Never used offset = 0x82; break; } offset += 0x9000; return offset; } static bool8 DrawStatusSymbol(u8 contestant) { bool8 statused = TRUE; u16 symbolOffset = 0; u8 contestantOffset = gContestantTurnOrder[contestant] * 5 + 2; if (eContestantStatus[contestant].resistant || eContestantStatus[contestant].immune || eContestantStatus[contestant].jamSafetyCount != 0 || eContestantStatus[contestant].jamReduction != 0) symbolOffset = GetStatusSymbolTileOffset(STAT_SYMBOL_CIRCLE); else if (eContestantStatus[contestant].nervous) symbolOffset = GetStatusSymbolTileOffset(STAT_SYMBOL_WAVE); else if (eContestantStatus[contestant].numTurnsSkipped != 0 || eContestantStatus[contestant].noMoreTurns) symbolOffset = GetStatusSymbolTileOffset(STAT_SYMBOL_X); else statused = FALSE; if (statused) { ContestBG_FillBoxWithIncrementingTile(0, symbolOffset, 20, contestantOffset, 2, 1, 17, 1); ContestBG_FillBoxWithIncrementingTile(0, symbolOffset + 16, 20, contestantOffset + 1, 2, 1, 17, 1); } else { ContestBG_FillBoxWithTile(0, 0, 20, contestantOffset, 2, 2, 17); } return statused; } static void DrawStatusSymbols(void) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) DrawStatusSymbol(i); } static void ContestClearGeneralTextWindow(void) { FillWindowPixelBuffer(WIN_GENERAL_TEXT, PIXEL_FILL(0)); CopyWindowToVram(WIN_GENERAL_TEXT, COPYWIN_GFX); Contest_SetBgCopyFlags(0); } static u16 GetChosenMove(u8 contestant) { if (Contest_IsMonsTurnDisabled(contestant)) return MOVE_NONE; if (contestant == gContestPlayerMonIndex) { return gContestMons[contestant].moves[eContest.playerMoveChoice]; } else { u8 moveChoice; ContestAI_ResetAI(contestant); moveChoice = ContestAI_GetActionToUse(); return gContestMons[contestant].moves[moveChoice]; } } static void GetAllChosenMoves(void) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) eContestantStatus[i].currMove = GetChosenMove(i); } static void RankContestants(void) { s32 i; s32 j; s16 arr[CONTESTANT_COUNT]; for (i = 0; i < CONTESTANT_COUNT; i++) { eContestantStatus[i].pointTotal += eContestantStatus[i].appeal; arr[i] = eContestantStatus[i].pointTotal; } // Sort the point totals using bubble-sort. for (i = 0; i < CONTESTANT_COUNT - 1; i++) { for (j = CONTESTANT_COUNT - 1; j > i; j--) { if (arr[j - 1] < arr[j]) { u16 temp; SWAP(arr[j], arr[j - 1], temp); } } } // For each contestant, find the best rank with their point total. // Normally, each point total is different, and this will output the // rankings as expected. However, if two pokemon are tied, then they // both get the best rank for that point total. // // For example if the point totals are [100, 80, 80, 50], the ranks will // be [1, 2, 2, 4]. The pokemon with a point total of 80 stop looking // when they see the first 80 in the array, so they both share the '2' // rank. for (i = 0; i < CONTESTANT_COUNT; i++) { for (j = 0; j < CONTESTANT_COUNT; j++) { if (eContestantStatus[i].pointTotal == arr[j]) { eContestantStatus[i].ranking = j; break; } } } SortContestants(TRUE); ApplyNextTurnOrder(); } static void SetAttentionLevels(void) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) { u8 attentionLevel; if (eContestantStatus[i].currMove == MOVE_NONE) attentionLevel = 5; else if (eContestantStatus[i].appeal <= 0) attentionLevel = 0; else if (eContestantStatus[i].appeal < 30) attentionLevel = 1; else if (eContestantStatus[i].appeal < 60) attentionLevel = 2; else if (eContestantStatus[i].appeal < 80) attentionLevel = 3; else attentionLevel = 4; eContestantStatus[i].attentionLevel = attentionLevel; } } static bool8 ContestantCanUseTurn(u8 contestant) { if (eContestantStatus[contestant].numTurnsSkipped != 0 || eContestantStatus[contestant].noMoreTurns) return FALSE; else return TRUE; } static void SetContestantStatusesForNextRound(void) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) { eContestantStatus[i].appeal = 0; eContestantStatus[i].baseAppeal = 0; eContestantStatus[i].jamSafetyCount = 0; if (eContestantStatus[i].numTurnsSkipped > 0) eContestantStatus[i].numTurnsSkipped--; eContestantStatus[i].jam = 0; eContestantStatus[i].resistant = 0; eContestantStatus[i].jamReduction = 0; eContestantStatus[i].immune = 0; eContestantStatus[i].moreEasilyStartled = 0; eContestantStatus[i].usedRepeatableMove = 0; eContestantStatus[i].nervous = FALSE; eContestantStatus[i].effectStringId = CONTEST_STRING_NONE; eContestantStatus[i].effectStringId2 = CONTEST_STRING_NONE; eContestantStatus[i].conditionMod = CONDITION_NO_CHANGE; eContestantStatus[i].repeatedPrevMove = eContestantStatus[i].repeatedMove; eContestantStatus[i].repeatedMove = FALSE; eContestantStatus[i].turnOrderModAction = 0; eContestantStatus[i].appealTripleCondition = 0; if (eContestantStatus[i].turnSkipped) { eContestantStatus[i].numTurnsSkipped = 1; eContestantStatus[i].turnSkipped = 0; } if (eContestantStatus[i].exploded) { eContestantStatus[i].noMoreTurns = TRUE; eContestantStatus[i].exploded = FALSE; } eContestantStatus[i].overrideCategoryExcitementMod = 0; } for (i = 0; i < CONTESTANT_COUNT; i++) { eContestantStatus[i].prevMove = eContestantStatus[i].currMove; eContest.moveHistory[eContest.appealNumber][i] = eContestantStatus[i].currMove; eContest.excitementHistory[eContest.appealNumber][i] = Contest_GetMoveExcitement(eContestantStatus[i].currMove); eContestantStatus[i].currMove = MOVE_NONE; } eContestExcitement.frozen = FALSE; } bool8 Contest_IsMonsTurnDisabled(u8 contestant) { if (eContestantStatus[contestant].numTurnsSkipped != 0 || eContestantStatus[contestant].noMoreTurns) return TRUE; else return FALSE; } static void CalculateTotalPointsForContestant(u8 contestant) { gContestMonRound2Points[contestant] = GetContestantRound2Points(contestant); gContestMonTotalPoints[contestant] = gContestMonRound1Points[contestant] + gContestMonRound2Points[contestant]; } static void CalculateFinalScores(void) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) CalculateTotalPointsForContestant(i); DetermineFinalStandings(); } static s16 GetContestantRound2Points(u8 contestant) { return gContestMonAppealPointTotals[contestant] * 2; } static void DetermineFinalStandings(void) { u16 randomOrdering[CONTESTANT_COUNT] = {0}; struct ContestFinalStandings standings[CONTESTANT_COUNT]; s32 i; // Seed random order in case of ties for (i = 0; i < CONTESTANT_COUNT; i++) { s32 j; randomOrdering[i] = Random(); for (j = 0; j < i; j++) { if (randomOrdering[i] == randomOrdering[j]) { i--; break; } } } // Init data for ranking contestants for (i = 0; i < CONTESTANT_COUNT; i++) { standings[i].totalPoints = gContestMonTotalPoints[i]; standings[i].round1Points = gContestMonRound1Points[i]; standings[i].random = randomOrdering[i]; standings[i].contestant = i; } // Rank contestants for (i = 0; i < CONTESTANT_COUNT - 1; i++) { s32 j; for (j = CONTESTANT_COUNT - 1; j > i; j--) { if (DidContestantPlaceHigher(j - 1, j, standings)) { // Swap contestants in array struct ContestFinalStandings temp; temp.totalPoints = standings[j - 1].totalPoints; temp.round1Points = standings[j - 1].round1Points; temp.random = standings[j - 1].random; temp.contestant = standings[j - 1].contestant; standings[j - 1].totalPoints = standings[j].totalPoints; standings[j - 1].round1Points = standings[j].round1Points; standings[j - 1].random = standings[j].random; standings[j - 1].contestant = standings[j].contestant; standings[j].totalPoints = temp.totalPoints; standings[j].round1Points = temp.round1Points; standings[j].random = temp.random; standings[j].contestant = temp.contestant; } } } // Assign placements. i is the placing (0 is 1st, 1 is 2nd...) for (i = 0; i < CONTESTANT_COUNT; i++) gContestFinalStandings[standings[i].contestant] = i; } void SaveLinkContestResults(void) { if ((gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK)) { gSaveBlock2Ptr->contestLinkResults[gSpecialVar_ContestCategory][gContestFinalStandings[gContestPlayerMonIndex]] = ((gSaveBlock2Ptr->contestLinkResults[gSpecialVar_ContestCategory][gContestFinalStandings[gContestPlayerMonIndex]] + 1) > 9999) ? 9999 : (gSaveBlock2Ptr->contestLinkResults[gSpecialVar_ContestCategory][gContestFinalStandings[gContestPlayerMonIndex]] + 1); } } static bool8 DidContestantPlaceHigher(s32 a, s32 b, struct ContestFinalStandings *standings) { bool8 retVal; // Rank contestants first based on total points if (standings[a].totalPoints < standings[b].totalPoints) retVal = TRUE; else if (standings[a].totalPoints > standings[b].totalPoints) retVal = FALSE; // If tied, rank on round 1 points else if (standings[a].round1Points < standings[b].round1Points) retVal = TRUE; else if (standings[a].round1Points > standings[b].round1Points) retVal = FALSE; // If tied again, choose randomly else if (standings[a].random < standings[b].random) retVal = TRUE; else retVal = FALSE; return retVal; } static void ContestPrintLinkStandby(void) { gBattle_BG0_Y = 0; gBattle_BG2_Y = 0; ContestClearGeneralTextWindow(); Contest_StartTextPrinter(gText_LinkStandby4, 0); } static void FillContestantWindowBgs(void) { int i; for(i = 0; i < CONTESTANT_COUNT; i++) ContestBG_FillBoxWithTile(0, 0, 0x16, 2 + i * 5, 8, 2, 0x11); } static u16 GetAppealHeartTileOffset(u8 contestant) { u16 offset; if (contestant == 0) offset = 0x5011; else if (contestant == 1) offset = 0x6011; else if (contestant == 2) offset = 0x7011; else offset = 0x8011; return offset + 1; } static s8 GetNumHeartsFromAppealPoints(s16 appeal) { s8 hearts = appeal / 10; if (hearts > 16) hearts = 16; else if (hearts < -16) hearts = -16; return hearts; } #define tNumHearts data[0] #define tHeartsDelta data[1] #define tHeartsSign data[2] #define tContestant data[3] #define tDelayTimer data[10] static u8 UpdateAppealHearts(s16 startAppeal, s16 appealDelta, u8 contestant) { u8 taskId; s8 startHearts; s8 heartsDelta; eContestGfxState[contestant].updatingAppealHearts = TRUE; taskId = CreateTask(Task_UpdateAppealHearts, 20); startHearts = GetNumHeartsFromAppealPoints(startAppeal); heartsDelta = GetNumHeartsFromAppealPoints(startAppeal + appealDelta) - startHearts; GetAppealHeartTileOffset(contestant); // unused return value gTasks[taskId].tNumHearts = abs(startHearts); gTasks[taskId].tHeartsDelta = heartsDelta; if (startHearts > 0 || (startHearts == 0 && heartsDelta > 0)) gTasks[taskId].tHeartsSign = 1; else gTasks[taskId].tHeartsSign = -1; gTasks[taskId].tContestant = contestant; return taskId; } static void Task_UpdateAppealHearts(u8 taskId) { u8 contestant = gTasks[taskId].tContestant; s16 startHearts = gTasks[taskId].tNumHearts; s16 heartsDelta = gTasks[taskId].tHeartsDelta; if (++gTasks[taskId].tDelayTimer > 14) { u16 heartOffset; u8 newNumHearts; u8 pitchMod; bool8 onSecondLine; gTasks[taskId].tDelayTimer = 0; if (gTasks[taskId].tHeartsDelta == 0) { // No more hearts to add/remove, end DestroyTask(taskId); eContestGfxState[contestant].updatingAppealHearts = FALSE; return; } else if (startHearts == 0) { if (heartsDelta < 0) { // Losing hearts, get black heart offset heartOffset = GetAppealHeartTileOffset(contestant) + 2; gTasks[taskId].tHeartsDelta++; } else { // Gaining hearts, get red heart offset heartOffset = GetAppealHeartTileOffset(contestant); gTasks[taskId].tHeartsDelta--; } newNumHearts = gTasks[taskId].tNumHearts++; } else { if (gTasks[taskId].tHeartsSign < 0) { // Hearts currently black (negative) if (heartsDelta < 0) { // Losing points, add black heart newNumHearts = gTasks[taskId].tNumHearts++; gTasks[taskId].tHeartsDelta++; heartOffset = GetAppealHeartTileOffset(contestant) + 2; } else { // Gaining points, remove black heart newNumHearts = --gTasks[taskId].tNumHearts; heartOffset = 0; gTasks[taskId].tHeartsDelta--; } } else { // Hearts currently red (positive) if (heartsDelta < 0) { // Losing points, remove red heart newNumHearts = --gTasks[taskId].tNumHearts; heartOffset = 0; gTasks[taskId].tHeartsDelta++; } else { // Gaining points, add red heart newNumHearts = gTasks[taskId].tNumHearts++; gTasks[taskId].tHeartsDelta--; heartOffset = GetAppealHeartTileOffset(contestant); } } } pitchMod = newNumHearts; onSecondLine = FALSE; // Check if wrapping to second line of hearts if (newNumHearts > 7) { onSecondLine = TRUE; newNumHearts -= 8; } ContestBG_FillBoxWithTile(0, heartOffset, newNumHearts + 22, gContestantTurnOrder[contestant] * 5 + 2 + onSecondLine, 1, 1, 17); if (heartsDelta > 0) { PlaySE(SE_CONTEST_HEART); m4aMPlayImmInit(&gMPlayInfo_SE1); m4aMPlayPitchControl(&gMPlayInfo_SE1, TRACKS_ALL, pitchMod * 256); } else { PlaySE(SE_BOO); } if (!onSecondLine && newNumHearts == 0 && heartOffset == 0) gTasks[taskId].tHeartsSign = -gTasks[taskId].tHeartsSign; } } static void CreateSliderHeartSprites(void) { s32 i; LoadSpriteSheet(&sSpriteSheet_SliderHeart); for (i = 0; i < CONTESTANT_COUNT; i++) { u8 y = sSliderHeartYPositions[gContestantTurnOrder[i]]; eContestGfxState[i].sliderHeartSpriteId = CreateSprite(&sSpriteTemplate_SliderHeart, 180, y, 1); } } #define sContestant data[0] #define sTargetX data[1] #define sMoveX data[2] static void UpdateHeartSlider(u8 contestant) { u8 spriteId; s16 slideTarget; eContestGfxState[contestant].sliderUpdating = TRUE; spriteId = eContestGfxState[contestant].sliderHeartSpriteId; slideTarget = eContestantStatus[contestant].pointTotal / 10 * 2; if (slideTarget > 56) slideTarget = 56; else if (slideTarget < 0) slideTarget = 0; gSprites[spriteId].invisible = FALSE; gSprites[spriteId].sContestant = contestant; gSprites[spriteId].sTargetX = slideTarget; if (gSprites[spriteId].sTargetX > gSprites[spriteId].x2) gSprites[spriteId].sMoveX = 1; else gSprites[spriteId].sMoveX = -1; gSprites[spriteId].callback = SpriteCB_UpdateHeartSlider; } static void UpdateHeartSliders(void) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) UpdateHeartSlider(i); } static bool8 SlidersDoneUpdating(void) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) { if (eContestGfxState[i].sliderUpdating) break; } if (i == CONTESTANT_COUNT) return TRUE; else return FALSE; } static void SpriteCB_UpdateHeartSlider(struct Sprite *sprite) { if (sprite->x2 == sprite->sTargetX) { eContestGfxState[sprite->sContestant].sliderUpdating = FALSE; sprite->callback = SpriteCallbackDummy; } else { sprite->x2 += sprite->sMoveX; } } #undef sContestant #undef sTargetX #undef sMoveX // Y positions change as the contestants change order static void UpdateSliderHeartSpriteYPositions(void) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) gSprites[eContestGfxState[i].sliderHeartSpriteId].y = sSliderHeartYPositions[gContestantTurnOrder[i]]; } // Used to hide (or subsequently reshow) the bottom two slider hearts that get hidden by text windows by moving them offscreen static void SetBottomSliderHeartsInvisibility(bool8 invisible) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) { // Skip the top two contestants sliders if (gContestantTurnOrder[i] > 1) { if (!invisible) gSprites[eContestGfxState[i].sliderHeartSpriteId].x = 180; else gSprites[eContestGfxState[i].sliderHeartSpriteId].x = 256; } } } static void CreateNextTurnSprites(void) { s32 i; LoadSpritePalette(&sSpritePalette_NextTurn); for (i = 0; i < CONTESTANT_COUNT; i++) { LoadCompressedSpriteSheet(&sSpriteSheet_NextTurn[i]); eContestGfxState[i].nextTurnSpriteId = CreateSprite(&sSpriteTemplates_NextTurn[i], 204, sNextTurnSpriteYPositions[gContestantTurnOrder[i]], 0); SetSubspriteTables(&gSprites[eContestGfxState[i].nextTurnSpriteId], sSubspriteTable_NextTurn); gSprites[eContestGfxState[i].nextTurnSpriteId].invisible = TRUE; } } static void CreateApplauseMeterSprite(void) { u8 spriteId; LoadCompressedSpriteSheet(&sSpriteSheet_ApplauseMeter); LoadSpritePalette(&sSpritePalette_ApplauseMeter); spriteId = CreateSprite(&sSpriteTemplate_ApplauseMeter, 30, 44, 1); gSprites[spriteId].invisible = TRUE; eContest.applauseMeterSpriteId = spriteId; } static void CreateJudgeAttentionEyeTask(void) { u8 i; u8 taskId = CreateTask(Task_FlashJudgeAttentionEye, 30); eContest.judgeAttentionTaskId = taskId; for (i = 0; i < CONTESTANT_COUNT; i++) gTasks[taskId].data[i * 4] = 0xFF; } static void StartFlashJudgeAttentionEye(u8 contestant) { gTasks[eContest.judgeAttentionTaskId].data[contestant * 4 + 0] = 0; gTasks[eContest.judgeAttentionTaskId].data[contestant * 4 + 1] = 0; } static void StopFlashJudgeAttentionEye(u8 contestant) { u8 taskId = CreateTask(Task_StopFlashJudgeAttentionEye, 31); gTasks[taskId].data[0] = contestant; } static void Task_StopFlashJudgeAttentionEye(u8 taskId) { u8 contestant = gTasks[taskId].data[0]; if (gTasks[eContest.judgeAttentionTaskId].data[contestant * 4 + 0] == 0 || gTasks[eContest.judgeAttentionTaskId].data[contestant * 4 + 0] == 0xFF) { gTasks[eContest.judgeAttentionTaskId].data[contestant * 4 + 0] = 0xFF; gTasks[eContest.judgeAttentionTaskId].data[contestant * 4 + 1] = 0; BlendPalette((eContest.prevTurnOrder[contestant] + 5) * 16 + 6, 2, 0, RGB(31, 31, 18)); DestroyTask(taskId); } } static void Task_FlashJudgeAttentionEye(u8 taskId) { u8 i; for (i = 0; i < CONTESTANT_COUNT; i++) { u8 offset = i * 4; if (gTasks[taskId].data[offset + 0] != 0xFF) { if (gTasks[taskId].data[offset + 1] == 0) gTasks[taskId].data[offset + 0]++; else gTasks[taskId].data[offset + 0]--; if (gTasks[taskId].data[offset + 0] == 16 || gTasks[taskId].data[offset + 0] == 0) gTasks[taskId].data[offset + 1] ^= 1; BlendPalette((eContest.prevTurnOrder[i] + 5) * 16 + 6, 2, gTasks[taskId].data[offset + 0], RGB(31, 31, 18)); } } } // Note: While the below task is run for the entire Appeals portion of the contest, // because data[i * 4] is always 0xFF it never does anything // If turned on by setting that data between 0 and 16, it blends // an odd selection of palette colors (e.g. the text box, the appeal hearts // for only one contestant, the heart outlines in the move selection box, etc) // Given the similarities, it's possible this was an incorrect attempt // at something similar to what CreateJudgeAttentionEyeTask does static void CreateUnusedBlendTask(void) { s32 i; eContest.blendTaskId = CreateTask(Task_UnusedBlend, 30); for (i = 0; i < CONTESTANT_COUNT; i++) InitUnusedBlendTaskData(i); } static void InitUnusedBlendTaskData(u8 contestant) { gTasks[eContest.blendTaskId].data[contestant * 4] = 0xFF; gTasks[eContest.blendTaskId].data[contestant * 4 + 1] = 0; } static void UpdateBlendTaskContestantsData(void) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) UpdateBlendTaskContestantData(i); } static void UpdateBlendTaskContestantData(u8 contestant) { u32 palOffset1; u32 palOffset2; InitUnusedBlendTaskData(contestant); palOffset1 = contestant + 5; DmaCopy16Defvars(3, gPlttBufferUnfaded + palOffset1 * 16 + 10, gPlttBufferFaded + palOffset1 * 16 + 10, 2); palOffset2 = (contestant + 5) * 16 + 12 + contestant; DmaCopy16Defvars(3, gPlttBufferUnfaded + palOffset2, gPlttBufferFaded + palOffset2, 2); } // See comments on CreateUnusedBlendTask static void Task_UnusedBlend(u8 taskId) { u8 i; for (i = 0; i < CONTESTANT_COUNT; i++) { u8 idx = i * 4; // Below is never true if (gTasks[taskId].data[idx] != 0xFF) { if (++gTasks[taskId].data[idx + 2] > 2) { gTasks[taskId].data[idx + 2] = 0; if (gTasks[taskId].data[idx + 1] == 0) gTasks[taskId].data[idx]++; else gTasks[taskId].data[idx]--; if (gTasks[taskId].data[idx] == 16 || gTasks[taskId].data[idx] == 0) gTasks[taskId].data[idx + 1] ^= 1; BlendPalette((i + 5) * 16 + 10, 1, gTasks[taskId].data[idx + 0], RGB(31, 31, 18)); BlendPalette((i + 5) * 16 + 12 + i, 1, gTasks[taskId].data[idx + 0], RGB(31, 31, 18)); } } } } static void StartStopFlashJudgeAttentionEye(u8 contestant) { if (eContestantStatus[contestant].hasJudgesAttention) StartFlashJudgeAttentionEye(contestant); else StopFlashJudgeAttentionEye(contestant); } static u8 CreateContestantBoxBlinkSprites(u8 contestant) { u8 spriteId1, spriteId2; u8 x = gContestantTurnOrder[contestant] * 40 + 32; LoadCompressedSpriteSheet(&sSpriteSheets_ContestantsTurnBlinkEffect[contestant]); LoadSpritePalette(&sSpritePalettes_ContestantsTurnBlinkEffect[contestant]); spriteId1 = CreateSprite(&sSpriteTemplates_ContestantsTurnBlinkEffect[contestant], 184, x, 29); spriteId2 = CreateSprite(&sSpriteTemplates_ContestantsTurnBlinkEffect[contestant], 248, x, 29); gSprites[spriteId2].oam.tileNum += 64; CopySpriteTiles(0, 3, (void *)VRAM, (u16 *)(BG_SCREEN_ADDR(28) + gContestantTurnOrder[contestant] * 5 * 64 + 0x26), gContestResources->boxBlinkTiles1); CopySpriteTiles(0, 3, (void *)VRAM, (u16 *)(BG_SCREEN_ADDR(28) + gContestantTurnOrder[contestant] * 5 * 64 + 0x36), gContestResources->boxBlinkTiles2); CpuFill32(0, gContestResources->boxBlinkTiles1 + 0x500, 0x300); CpuFill32(0, gContestResources->boxBlinkTiles2 + 0x500, 0x300); RequestDma3Copy(gContestResources->boxBlinkTiles1, (u8 *)(OBJ_VRAM0 + gSprites[spriteId1].oam.tileNum * 32), 0x800, 1); RequestDma3Copy(gContestResources->boxBlinkTiles2, (u8 *)(OBJ_VRAM0 + gSprites[spriteId2].oam.tileNum * 32), 0x800, 1); gSprites[spriteId1].data[0] = spriteId2; gSprites[spriteId2].data[0] = spriteId1; gSprites[spriteId1].data[1] = contestant; gSprites[spriteId2].data[1] = contestant; return spriteId1; } static void DestroyContestantBoxBlinkSprites(u8 spriteId) { u8 spriteId2 = gSprites[spriteId].data[0]; FreeSpriteOamMatrix(&gSprites[spriteId2]); DestroySprite(&gSprites[spriteId2]); DestroySpriteAndFreeResources(&gSprites[spriteId]); } static void SetBlendForContestantBoxBlink(void) { SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT2_ALL | BLDCNT_EFFECT_BLEND); SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(7, 9)); } static void ResetBlendForContestantBoxBlink(void) { SetGpuReg(REG_OFFSET_BLDCNT, 0); SetGpuReg(REG_OFFSET_BLDALPHA, 0); } // To indicate whose turn is up static void BlinkContestantBox(u8 spriteId, bool8 b) { u8 spriteId2; SetBlendForContestantBoxBlink(); eContestGfxState[gSprites[spriteId].data[1]].boxBlinking = TRUE; spriteId2 = gSprites[spriteId].data[0]; StartSpriteAffineAnim(&gSprites[spriteId], 1); StartSpriteAffineAnim(&gSprites[spriteId2], 1); gSprites[spriteId].callback = SpriteCB_BlinkContestantBox; gSprites[spriteId2].callback = SpriteCallbackDummy; if (b == FALSE) PlaySE(SE_CONTEST_MONS_TURN); else PlaySE(SE_PC_LOGIN); } static void SpriteCB_BlinkContestantBox(struct Sprite *sprite) { if (sprite->affineAnimEnded) { u8 spriteId2 = sprite->data[0]; if (gSprites[spriteId2].affineAnimEnded) { sprite->invisible = TRUE; gSprites[spriteId2].invisible = TRUE; sprite->callback = SpriteCB_EndBlinkContestantBox; } } } static void SpriteCB_EndBlinkContestantBox(struct Sprite *sprite) { eContestGfxState[sprite->data[1]].boxBlinking = FALSE; DestroyContestantBoxBlinkSprites(sprite->data[0]); ResetBlendForContestantBoxBlink(); } // Unused. static void ContestDebugTogglePointTotal(void) { if(eContestDebugMode == CONTEST_DEBUG_MODE_PRINT_POINT_TOTAL) eContestDebugMode = CONTEST_DEBUG_MODE_OFF; else eContestDebugMode = CONTEST_DEBUG_MODE_PRINT_POINT_TOTAL; if(eContestDebugMode == CONTEST_DEBUG_MODE_OFF) { DrawContestantWindowText(); SwapMoveDescAndContestTilemaps(); } else { ContestDebugDoPrint(); } } static void ContestDebugDoPrint(void) { u8 i; s16 value; u8 *txtPtr; u8 text[8]; if (!gEnableContestDebugging) return; switch (eContestDebugMode) { case CONTEST_DEBUG_MODE_OFF: break; case CONTEST_DEBUG_MODE_PRINT_WINNER_FLAGS: case CONTEST_DEBUG_MODE_PRINT_LOSER_FLAGS: ContestDebugPrintBitStrings(); break; // The only other possible value is 1, which is only set by ContestDebugTogglePointTotal. // // case CONTEST_DEBUG_MODE_PRINT_POINT_TOTAL: default: for (i = 0; i < CONTESTANT_COUNT; i++) FillWindowPixelBuffer(i, PIXEL_FILL(0)); for (i = 0; i < CONTESTANT_COUNT; i++) { value = eContestantStatus[i].pointTotal; txtPtr = text; if (eContestantStatus[i].pointTotal < 0) { value *= -1; txtPtr = StringCopy(txtPtr, gText_OneDash); } ConvertIntToDecimalStringN(txtPtr, value, STR_CONV_MODE_LEFT_ALIGN, 4); Contest_PrintTextToBg0WindowAt(gContestantTurnOrder[i], text, 55, 1, FONT_NARROW); } for (i = 0; i < CONTESTANT_COUNT; i++) { value = eContestantStatus[i].appeal; txtPtr = text; if (eContestantStatus[i].appeal < 0) { value *= -1; txtPtr = StringCopy(txtPtr, gText_OneDash); } ConvertIntToDecimalStringN(txtPtr, value, STR_CONV_MODE_LEFT_ALIGN, 4); Contest_PrintTextToBg0WindowAt(gContestantTurnOrder[i], text, 5, 1, FONT_NARROW); } SwapMoveDescAndContestTilemaps(); break; } } void SortContestants(bool8 useRanking) { u8 scratch[CONTESTANT_COUNT]; u16 randomOrdering[CONTESTANT_COUNT] = {0}; s32 i; s32 v3; // Generate a unique random number for each contestant. for (i = 0; i < CONTESTANT_COUNT; i++) { s32 j; randomOrdering[i] = Random(); // Loop through all the numbers generated so far. for (j = 0; j < i; j++) { if (randomOrdering[i] == randomOrdering[j]) { // This number isn't unique; try generating again. i--; break; } } } if (!useRanking) { // Order based on the results of the Conditions round using Insertion Sort. // Use the randomOrdering to break ties. for (i = 0; i < CONTESTANT_COUNT; i++) { // Append this contestant to the list. gContestantTurnOrder[i] = i; // Determine where the contestant should be ordered. for (v3 = 0; v3 < i; v3++) { if (gContestMonRound1Points[gContestantTurnOrder[v3]] < gContestMonRound1Points[i] || (gContestMonRound1Points[gContestantTurnOrder[v3]] == gContestMonRound1Points[i] && randomOrdering[gContestantTurnOrder[v3]] < randomOrdering[i])) { // Shift everything larger up to make room. s32 j; for (j = i; j > v3; j--) gContestantTurnOrder[j] = gContestantTurnOrder[j - 1]; // Insert into the new spot. gContestantTurnOrder[v3] = i; break; } } // This is redundant. // Perhaps GF switched from true insertion sort to in-place insertion sort, and forgot to // remove this check? if (v3 == i) gContestantTurnOrder[i] = i; } // Invert gContestantTurnOrder; above, it was a list of contestant IDs. Now it's a list of turn orderings. // // For example, if contestant 3 had the first turn, then `gContestantTurnOrder[1] = 3`. The turn is the index, // the contestant is the data. After inverting the list, `gContestantTurnOrder[3] = 1`. The contestant is the index, // and the turn is the data. memcpy(scratch, gContestantTurnOrder, sizeof(scratch)); for (i = 0; i < CONTESTANT_COUNT; i++) gContestantTurnOrder[scratch[i]] = i; } else { // Order contestants based on their ranking. // If contestants have tied ranking, fill in the next available slot. // // Note that ranking is calculated so that shared places still take up a ranking // space. A ranking like [1, 2, 2, 3] is not possible; it would be [1, 2, 2, 4] // instead. memset(scratch, 0xFF, sizeof(scratch)); for (i = 0; i < CONTESTANT_COUNT; i++) { u8 j = eContestantStatus[i].ranking; while (1) { u8 *ptr = &scratch[j]; if (*ptr == 0xFF) { *ptr = i; gContestantTurnOrder[i] = j; break; } j++; } } // Randomize the order of contestants with tied rankings using Selection Sort. // // Look through the array for tied ranks, and use randomOrdering to break the tie. // This ensures that contestants with the same rank will be randomly ordered. This // uses an in-place slection sort, which involves a lot of extra swapping. for (i = 0; i < CONTESTANT_COUNT - 1; i++) { for (v3 = CONTESTANT_COUNT - 1; v3 > i; v3--) { if (eContestantStatus[v3 - 1].ranking == eContestantStatus[v3].ranking && gContestantTurnOrder[v3 - 1] < gContestantTurnOrder[v3] && randomOrdering[v3 - 1] < randomOrdering[v3]) { u8 temp = gContestantTurnOrder[v3]; gContestantTurnOrder[v3] = gContestantTurnOrder[v3 - 1]; gContestantTurnOrder[v3 - 1] = temp; } } } } } static void DrawContestantWindows(void) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) { s32 windowId = i + 5; LoadPalette(eContestTempSave.cachedWindowPalettes[windowId], (gContestantTurnOrder[i] + 5) * 16, sizeof(eContestTempSave.cachedWindowPalettes[0])); } DrawContestantWindowText(); } static void CalculateAppealMoveImpact(u8 contestant) { u16 move; u8 effect; u8 rnd; s32 i; eContestantStatus[contestant].appeal = 0; eContestantStatus[contestant].baseAppeal = 0; if (!ContestantCanUseTurn(contestant)) return; move = eContestantStatus[contestant].currMove; effect = gContestMoves[move].effect; eContestantStatus[contestant].moveCategory = gContestMoves[eContestantStatus[contestant].currMove].contestCategory; if (eContestantStatus[contestant].currMove == eContestantStatus[contestant].prevMove && eContestantStatus[contestant].currMove != MOVE_NONE) { eContestantStatus[contestant].repeatedMove = TRUE; eContestantStatus[contestant].moveRepeatCount++; } else { eContestantStatus[contestant].moveRepeatCount = 0; } eContestantStatus[contestant].baseAppeal = gContestEffects[effect].appeal; eContestantStatus[contestant].appeal = eContestantStatus[contestant].baseAppeal; eContestAppealResults.jam = gContestEffects[effect].jam; eContestAppealResults.jam2 = eContestAppealResults.jam; eContestAppealResults.contestant = contestant; for (i = 0; i < CONTESTANT_COUNT; i++) { eContestantStatus[i].jam = 0; eContestAppealResults.unnervedPokes[i] = 0; } if (eContestantStatus[contestant].hasJudgesAttention && !AreMovesContestCombo(eContestantStatus[contestant].prevMove, eContestantStatus[contestant].currMove)) eContestantStatus[contestant].hasJudgesAttention = FALSE; gContestEffectFuncs[effect](); if (eContestantStatus[contestant].conditionMod == CONDITION_GAIN) eContestantStatus[contestant].appeal += eContestantStatus[contestant].condition - 10; else if (eContestantStatus[contestant].appealTripleCondition) eContestantStatus[contestant].appeal += eContestantStatus[contestant].condition * 3; else eContestantStatus[contestant].appeal += eContestantStatus[contestant].condition; eContestantStatus[contestant].completedCombo = FALSE; eContestantStatus[contestant].usedComboMove = FALSE; if (IsContestantAllowedToCombo(contestant)) { bool8 completedCombo = AreMovesContestCombo(eContestantStatus[contestant].prevMove, eContestantStatus[contestant].currMove); if (completedCombo && eContestantStatus[contestant].hasJudgesAttention) { eContestantStatus[contestant].completedCombo = completedCombo; eContestantStatus[contestant].usedComboMove = TRUE; eContestantStatus[contestant].hasJudgesAttention = FALSE; eContestantStatus[contestant].comboAppealBonus = eContestantStatus[contestant].baseAppeal * eContestantStatus[contestant].completedCombo; eContestantStatus[contestant].completedComboFlag = TRUE; // Redundant with completedCombo, used by AI } else { if (gContestMoves[eContestantStatus[contestant].currMove].comboStarterId != 0) { eContestantStatus[contestant].hasJudgesAttention = TRUE; eContestantStatus[contestant].usedComboMove = TRUE; } else { eContestantStatus[contestant].hasJudgesAttention = FALSE; } } } if (eContestantStatus[contestant].repeatedMove) eContestantStatus[contestant].repeatJam = (eContestantStatus[contestant].moveRepeatCount + 1) * 10; if (eContestantStatus[contestant].nervous) { eContestantStatus[contestant].hasJudgesAttention = FALSE; eContestantStatus[contestant].appeal = 0; eContestantStatus[contestant].baseAppeal = 0; } eContestExcitement.moveExcitement = Contest_GetMoveExcitement(eContestantStatus[contestant].currMove); if (eContestantStatus[contestant].overrideCategoryExcitementMod) eContestExcitement.moveExcitement = 1; if (eContestExcitement.moveExcitement > 0) { if (eContest.applauseLevel + eContestExcitement.moveExcitement > 4) eContestExcitement.excitementAppealBonus = 60; else eContestExcitement.excitementAppealBonus = 10; } else { eContestExcitement.excitementAppealBonus = 0; } // Transform and Role Play require a visible target mon // so randomly choose a contestant to be the "target" rnd = Random() % (CONTESTANT_COUNT - 1); for (i = 0; i < CONTESTANT_COUNT; i++) { // Target can't be the attacker if (i != contestant) { if (rnd == 0) break; rnd--; } } eContestantStatus[contestant].contestantAnimTarget = i; } void SetContestantEffectStringID(u8 a, u8 b) { eContestantStatus[a].effectStringId = b; } void SetContestantEffectStringID2(u8 a, u8 b) { eContestantStatus[a].effectStringId2 = b; } void SetStartledString(u8 contestant, u8 jam) { if (jam >= 60) SetContestantEffectStringID(contestant, CONTEST_STRING_TRIPPED_OVER); else if (jam >= 40) SetContestantEffectStringID(contestant, CONTEST_STRING_LEAPT_UP); else if (jam >= 30) SetContestantEffectStringID(contestant, CONTEST_STRING_UTTER_CRY); else if (jam >= 20) SetContestantEffectStringID(contestant, CONTEST_STRING_TURNED_BACK); else if (jam >= 10) SetContestantEffectStringID(contestant, CONTEST_STRING_LOOKED_DOWN); } static void PrintAppealMoveResultText(u8 contestant, u8 stringId) { StringCopy(gStringVar1, gContestMons[contestant].nickname); StringCopy(gStringVar2, gMoveNames[eContestantStatus[contestant].currMove]); if (gContestMoves[eContestantStatus[eContestAppealResults.contestant].currMove].contestCategory == CONTEST_CATEGORY_COOL) StringCopy(gStringVar3, gText_Contest_Shyness); else if (gContestMoves[eContestantStatus[eContestAppealResults.contestant].currMove].contestCategory == CONTEST_CATEGORY_BEAUTY) StringCopy(gStringVar3, gText_Contest_Anxiety); else if (gContestMoves[eContestantStatus[eContestAppealResults.contestant].currMove].contestCategory == CONTEST_CATEGORY_CUTE) StringCopy(gStringVar3, gText_Contest_Laziness); else if (gContestMoves[eContestantStatus[eContestAppealResults.contestant].currMove].contestCategory == CONTEST_CATEGORY_SMART) StringCopy(gStringVar3, gText_Contest_Hesitancy); else StringCopy(gStringVar3, gText_Contest_Fear); StringExpandPlaceholders(gStringVar4, sAppealResultTexts[stringId]); ContestClearGeneralTextWindow(); Contest_StartTextPrinter(gStringVar4, 1); } void MakeContestantNervous(u8 p) { eContestantStatus[p].nervous = TRUE; eContestantStatus[p].currMove = MOVE_NONE; } // This function calculates the new turn order for the next round. The // algorithm first checks for explicit turn assignments in the // ContestantStatus::nextTurnOrder field of each contestant. The remaining // turns are assigned such that the turn order will reverse. // // For example, if no pokemon have a defined nextTurnOrder, then the 4th // will become 1st, the 3rd will become 2nd, etc. // // Note: This function assumes that multiple pokemon cannot have the same // nextTurnOrder value. static void ApplyNextTurnOrder(void) { u8 nextContestant = 0; s32 i; s32 j; u8 newTurnOrder[CONTESTANT_COUNT]; bool8 isContestantOrdered[CONTESTANT_COUNT]; // Copy the current turn order. for (i = 0; i < CONTESTANT_COUNT; i++) { newTurnOrder[i] = gContestantTurnOrder[i]; isContestantOrdered[i] = FALSE; } // For each turn, assign a contestant to that turn. for (i = 0; i < CONTESTANT_COUNT; i++) { // Look for explicit turn assignments. for (j = 0; j < CONTESTANT_COUNT; j++) { if (eContestantStatus[j].nextTurnOrder == i) { newTurnOrder[j] = i; isContestantOrdered[j] = TRUE; break; } } if (j == CONTESTANT_COUNT) { // No contestant was assigned to this turn. Look for the unassigned contestant // with the highest turn order. // // First, look for the first unassigned contestant. for (j = 0; j < CONTESTANT_COUNT; j++) { if (!isContestantOrdered[j] && eContestantStatus[j].nextTurnOrder == 0xFF) { nextContestant = j; j++; break; } } // Then, look for a better candidate, with a higher turn order. for (; j < CONTESTANT_COUNT; j++) { if (!isContestantOrdered[j] && eContestantStatus[j].nextTurnOrder == 0xFF && gContestantTurnOrder[nextContestant] > gContestantTurnOrder[j]) nextContestant = j; } // Assign the contestant to this turn. newTurnOrder[nextContestant] = i; isContestantOrdered[nextContestant] = TRUE; } } for (i = 0; i < CONTESTANT_COUNT; i++) { eContestAppealResults.turnOrder[i] = newTurnOrder[i]; eContestantStatus[i].nextTurnOrder = 0xFF; eContestantStatus[i].turnOrderMod = 0; gContestantTurnOrder[i] = newTurnOrder[i]; } } static void SpriteCB_JudgeSpeechBubble(struct Sprite *sprite) { if (sprite->data[1]++ > 84) { sprite->data[1] = 0; sprite->invisible = TRUE; sprite->callback = SpriteCallbackDummy; eContest.waitForJudgeSpeechBubble = FALSE; } } static void DoJudgeSpeechBubble(u8 symbolId) { u8 spriteId = eContest.judgeSpeechBubbleSpriteId; switch (symbolId) { case JUDGE_SYMBOL_SWIRL: case JUDGE_SYMBOL_SWIRL_UNUSED: gSprites[spriteId].oam.tileNum = gSprites[spriteId].data[0]; PlaySE(SE_FAILURE); break; case JUDGE_SYMBOL_ONE_EXCLAMATION: gSprites[spriteId].oam.tileNum = gSprites[spriteId].data[0] + 4; PlaySE(SE_SUCCESS); break; case JUDGE_SYMBOL_TWO_EXCLAMATIONS: gSprites[spriteId].oam.tileNum = gSprites[spriteId].data[0] + 8; PlaySE(SE_SUCCESS); break; case JUDGE_SYMBOL_NUMBER_ONE_UNUSED: // Identical to JUDGE_SYMBOL_NUMBER_ONE gSprites[spriteId].oam.tileNum = gSprites[spriteId].data[0] + 12; PlaySE(SE_WARP_IN); break; case JUDGE_SYMBOL_NUMBER_ONE: gSprites[spriteId].oam.tileNum = gSprites[spriteId].data[0] + 12; PlaySE(SE_WARP_IN); break; case JUDGE_SYMBOL_NUMBER_FOUR: gSprites[spriteId].oam.tileNum = gSprites[spriteId].data[0] + 16; PlaySE(SE_WARP_IN); break; case JUDGE_SYMBOL_STAR: gSprites[spriteId].oam.tileNum = gSprites[spriteId].data[0] + 24; PlaySE(SE_M_HEAL_BELL); break; case JUDGE_SYMBOL_QUESTION_MARK: default: gSprites[spriteId].oam.tileNum = gSprites[spriteId].data[0] + 20; PlaySE(SE_WARP_IN); break; } gSprites[spriteId].data[1] = 0; gSprites[spriteId].invisible = FALSE; gSprites[spriteId].callback = SpriteCB_JudgeSpeechBubble; eContest.waitForJudgeSpeechBubble = TRUE; } static void UpdateApplauseMeter(void) { s32 i; for (i = 0; i < APPLAUSE_METER_SIZE; i++) { const u8 *src; if (i < eContest.applauseLevel) src = &gContestApplauseMeterGfx[64]; else src = gContestApplauseMeterGfx; CpuCopy32(src, (void *)(OBJ_VRAM0 + (gSprites[eContest.applauseMeterSpriteId].oam.tileNum + 17 + i) * 32), 32); CpuCopy32(src + 32, (void *)(OBJ_VRAM0 + (gSprites[eContest.applauseMeterSpriteId].oam.tileNum + 25 + i) * 32), 32); if (eContest.applauseLevel > 4) StartApplauseOverflowAnimation(); } } s8 Contest_GetMoveExcitement(u16 move) { return gContestExcitementTable[gSpecialVar_ContestCategory][gContestMoves[move].contestCategory]; } static u8 StartApplauseOverflowAnimation(void) { u8 taskId = CreateTask(Task_ApplauseOverflowAnimation, 10); gTasks[taskId].data[1] = 1; gTasks[taskId].data[2] = IndexOfSpritePaletteTag(TAG_APPLAUSE_METER); return taskId; } static void Task_ApplauseOverflowAnimation(u8 taskId) { // Skip every other frame. if (++gTasks[taskId].data[0] == 1) { gTasks[taskId].data[0] = 0; // Alternate between normal colors and white. if (gTasks[taskId].data[3] == 0) gTasks[taskId].data[4]++; else gTasks[taskId].data[4]--; BlendPalette(264 + gTasks[taskId].data[2] * 16, 1, gTasks[taskId].data[4], RGB_WHITE); // At the maximum or minimum blending, switch directions. if (gTasks[taskId].data[4] == 0 || gTasks[taskId].data[4] == 16) { gTasks[taskId].data[3] ^= 1; // Continue the animation until the applause meter is cleared. if (eContest.applauseLevel < 5) { BlendPalette(264 + gTasks[taskId].data[2] * 16, 1, 0, RGB_RED); DestroyTask(taskId); } } } } static void SlideApplauseMeterIn(void) { CreateTask(Task_SlideApplauseMeterIn, 10); gSprites[eContest.applauseMeterSpriteId].x2 = -70; gSprites[eContest.applauseMeterSpriteId].invisible = FALSE; eContest.applauseMeterIsMoving = TRUE; } static void Task_SlideApplauseMeterIn(u8 taskId) { struct Sprite *sprite = &gSprites[eContest.applauseMeterSpriteId]; gTasks[taskId].data[10] += 1664; sprite->x2 += gTasks[taskId].data[10] >> 8; gTasks[taskId].data[10] = gTasks[taskId].data[10] & 0xFF; if (sprite->x2 > 0) sprite->x2 = 0; if (sprite->x2 == 0) { eContest.applauseMeterIsMoving = FALSE; DestroyTask(taskId); } } static void SlideApplauseMeterOut(void) { if (gSprites[eContest.applauseMeterSpriteId].invisible == TRUE) { eContest.applauseMeterIsMoving = FALSE; } else { CreateTask(Task_SlideApplauseMeterOut, 10); gSprites[eContest.applauseMeterSpriteId].x2 = 0; eContest.applauseMeterIsMoving = TRUE; } } static void Task_SlideApplauseMeterOut(u8 taskId) { struct Sprite *sprite = &gSprites[eContest.applauseMeterSpriteId]; gTasks[taskId].data[10] += 1664; sprite->x2 -= gTasks[taskId].data[10] >> 8; gTasks[taskId].data[10] = gTasks[taskId].data[10] & 0xFF; if (sprite->x2 < -70) sprite->x2 = -70; if (sprite->x2 == -70) { sprite->invisible = TRUE; eContest.applauseMeterIsMoving = FALSE; DestroyTask(taskId); } } static void ShowAndUpdateApplauseMeter(s8 unused) { u8 taskId = CreateTask(Task_ShowAndUpdateApplauseMeter, 5); gTasks[taskId].data[0] = unused; eContest.isShowingApplauseMeter = TRUE; } static void Task_ShowAndUpdateApplauseMeter(u8 taskId) { switch (gTasks[taskId].data[10]) { case 0: SlideApplauseMeterIn(); gTasks[taskId].data[10]++; break; case 1: if (!eContest.applauseMeterIsMoving) { gTasks[taskId].data[10]++; } break; case 2: if (gTasks[taskId].data[11]++ > 20) { gTasks[taskId].data[11] = 0; UpdateApplauseMeter(); eContest.isShowingApplauseMeter = FALSE; DestroyTask(taskId); } break; } } // Unused. static void HideApplauseMeterNoAnim(void) { gSprites[eContest.applauseMeterSpriteId].x2 = 0; gSprites[eContest.applauseMeterSpriteId].invisible = FALSE; } // Unused. static void ShowApplauseMeterNoAnim(void) { gSprites[eContest.applauseMeterSpriteId].invisible = TRUE; } #define tDelay data[10] #define tFrame data[11] #define tCycles data[12] static void AnimateAudience(void) { CreateTask(Task_AnimateAudience, 15); eContest.animatingAudience = TRUE; } static void Task_AnimateAudience(u8 taskId) { if (gTasks[taskId].tDelay++ > 6) { gTasks[taskId].tDelay = 0; if (gTasks[taskId].tFrame == 0) { RequestDma3Copy(eContestAudienceFrame2_Gfx, (void *)(BG_SCREEN_ADDR(4)), 0x1000, 1); } else { RequestDma3Copy(eUnzippedContestAudience_Gfx, (void *)(BG_SCREEN_ADDR(4)), 0x1000, 1); gTasks[taskId].tCycles++; } gTasks[taskId].tFrame ^= 1; if (gTasks[taskId].tCycles == 9) { eContest.animatingAudience = FALSE; DestroyTask(taskId); } } } #undef tDelay #undef tFrame #undef tCycles #define tBlendColor data[0] #define tBlendCoeff data[1] #define tBlendDir data[2] #define tTargetBlendCoeff data[3] #define tBlendDelay data[10] static void BlendAudienceBackground(s8 excitementDir, s8 blendDir) { u8 taskId = CreateTask(Task_BlendAudienceBackground, 10); u16 blendColor; u8 blendCoeff; u8 targetBlendCoeff; if (excitementDir > 0) { blendColor = RGB(30, 27, 8); if (blendDir > 0) { // Blend to yellow (amount depends on applause meter) blendCoeff = 0; targetBlendCoeff = eContest.applauseLevel * 3; } else { // Blend back to original blendCoeff = eContest.applauseLevel * 3; targetBlendCoeff = 0; } } else { blendColor = RGB_BLACK; if (blendDir > 0) { // Blend to black blendCoeff = 0; targetBlendCoeff = 12; } else { // Black back to original blendCoeff = 12; targetBlendCoeff = 0; } } gTasks[taskId].tBlendColor = blendColor; gTasks[taskId].tBlendCoeff = blendCoeff; gTasks[taskId].tBlendDir = blendDir; gTasks[taskId].tTargetBlendCoeff = targetBlendCoeff; // Because this isn't set to TRUE here, the main task doesn't wait for the color blend // Unclear if this was intentional or not (perhaps waiting added too much delay). In any case it does nothing now eContest.waitForAudienceBlend = FALSE; } static void Task_BlendAudienceBackground(u8 taskId) { if (gTasks[taskId].tBlendDelay++ >= 0) { gTasks[taskId].tBlendDelay = 0; if (gTasks[taskId].tBlendDir > 0) gTasks[taskId].tBlendCoeff++; else gTasks[taskId].tBlendCoeff--; BlendPalette(17, 1, gTasks[taskId].tBlendCoeff, gTasks[taskId].tBlendColor); BlendPalette(26, 1, gTasks[taskId].tBlendCoeff, gTasks[taskId].tBlendColor); if (gTasks[taskId].tBlendCoeff == gTasks[taskId].tTargetBlendCoeff) { DestroyTask(taskId); eContest.waitForAudienceBlend = FALSE; } } } #undef tBlendColor #undef tBlendCoeff #undef tTargetBlendCoeff #undef tBlendDelay static void ShowHideNextTurnGfx(bool8 show) { s32 i; for (i = 0; i < CONTESTANT_COUNT; i++) { if (eContestantStatus[i].turnOrderMod != 0 && show) { CpuCopy32(GetTurnOrderNumberGfx(i), (void *)(OBJ_VRAM0 + (gSprites[eContestGfxState[i].nextTurnSpriteId].oam.tileNum + 6) * 32), 32); gSprites[eContestGfxState[i].nextTurnSpriteId].y = sNextTurnSpriteYPositions[gContestantTurnOrder[i]]; gSprites[eContestGfxState[i].nextTurnSpriteId].invisible = FALSE; } else { gSprites[eContestGfxState[i].nextTurnSpriteId].invisible = TRUE; } } } static const u8 *GetTurnOrderNumberGfx(u8 contestant) { if (eContestantStatus[contestant].turnOrderMod != 1) return gContestNextTurnRandomGfx; else return gContestNextTurnNumbersGfx + eContestantStatus[contestant].nextTurnOrder * 32; } static void DrawUnnervedSymbols(void) { s32 i = 0; for (i = 0; i < CONTESTANT_COUNT; i++) { if (eContestAppealResults.unnervedPokes[i] != 0 && !Contest_IsMonsTurnDisabled(i)) { u32 contestantOffset = gContestantTurnOrder[i] * 5 + 2; u16 symbolOffset = GetStatusSymbolTileOffset(STAT_SYMBOL_SWIRL); ContestBG_FillBoxWithIncrementingTile(0, symbolOffset, 20, contestantOffset, 2, 1, 17, 1); symbolOffset += 16; ContestBG_FillBoxWithIncrementingTile(0, symbolOffset, 20, contestantOffset + 1, 2, 1, 17, 1); PlaySE(SE_CONTEST_ICON_CHANGE); } } } bool8 IsContestantAllowedToCombo(u8 contestant) { if (eContestantStatus[contestant].repeatedMove || eContestantStatus[contestant].nervous) return FALSE; else return TRUE; } static void SetBgForCurtainDrop(void) { s32 i; u16 bg0Cnt, bg1Cnt, bg2Cnt; bg1Cnt = GetGpuReg(REG_OFFSET_BG1CNT); ((vBgCnt *)&bg1Cnt)->priority = 0; ((vBgCnt *)&bg1Cnt)->screenSize = 2; ((vBgCnt *)&bg1Cnt)->areaOverflowMode = 0; ((vBgCnt *)&bg1Cnt)->charBaseBlock = 0; SetGpuReg(REG_OFFSET_BG1CNT, bg1Cnt); bg0Cnt = GetGpuReg(REG_OFFSET_BG0CNT); bg2Cnt = GetGpuReg(REG_OFFSET_BG2CNT); ((vBgCnt *)&bg0Cnt)->priority = 1; ((vBgCnt *)&bg2Cnt)->priority = 1; SetGpuReg(REG_OFFSET_BG0CNT, bg0Cnt); SetGpuReg(REG_OFFSET_BG2CNT, bg2Cnt); gBattle_BG1_X = DISPLAY_WIDTH; gBattle_BG1_Y = DISPLAY_HEIGHT; SetGpuReg(REG_OFFSET_BG1HOFS, gBattle_BG1_X); SetGpuReg(REG_OFFSET_BG1VOFS, gBattle_BG1_Y); CpuFill32(0, gContestResources->contestBgTilemaps[1], 0x1000); CopyToBgTilemapBuffer(1, gContestCurtainTilemap, 0, 0); Contest_SetBgCopyFlags(1); for (i = 0; i < CONTESTANT_COUNT; i++) { gSprites[eContestGfxState[i].sliderHeartSpriteId].oam.priority = 1; gSprites[eContestGfxState[i].nextTurnSpriteId].oam.priority = 1; } } static void UpdateContestantBoxOrder(void) { s32 i; u16 bg1Cnt; RequestDma3Fill(0,(void *)(BG_CHAR_ADDR(2)), 0x2000, 1); CpuFill32(0, gContestResources->contestBgTilemaps[1], 0x1000); Contest_SetBgCopyFlags(1); bg1Cnt = GetGpuReg(REG_OFFSET_BG1CNT); ((vBgCnt *) &bg1Cnt)->priority = 1; ((vBgCnt *) &bg1Cnt)->screenSize = 0; ((vBgCnt *) &bg1Cnt)->areaOverflowMode = 0; ((vBgCnt *) &bg1Cnt)->charBaseBlock = 2; SetGpuReg(REG_OFFSET_BG1CNT, bg1Cnt); gBattle_BG1_X = 0; gBattle_BG1_Y = 0; for (i = 0; i < CONTESTANT_COUNT; i++) { gSprites[eContestGfxState[i].sliderHeartSpriteId].oam.priority = 0; gSprites[eContestGfxState[i].nextTurnSpriteId].oam.priority = 0; } } static void Task_StartDropCurtainAtRoundEnd(u8 taskId) { gBattle_BG1_X = 0; gBattle_BG1_Y = DISPLAY_HEIGHT; PlaySE12WithPanning(SE_CONTEST_CURTAIN_FALL, 0); gTasks[taskId].func = Task_UpdateCurtainDropAtRoundEnd; } static void Task_UpdateCurtainDropAtRoundEnd(u8 taskId) { if ((s16)(gBattle_BG1_Y -= 7) < 0) gBattle_BG1_Y = 0; if (gBattle_BG1_Y == 0) { gTasks[taskId].data[0] = 0; gTasks[taskId].data[1] = 0; gTasks[taskId].data[2] = 0; gTasks[taskId].func = Task_ResetForNextRound; } } static void Task_ResetForNextRound(u8 taskId) { s32 i; switch (gTasks[taskId].data[0]) { case 0: for (i = 0; i < CONTESTANT_COUNT; i++) eContest.prevTurnOrder[i] = gContestantTurnOrder[i]; FillContestantWindowBgs(); UpdateBlendTaskContestantsData(); DrawConditionStars(); DrawContestantWindows(); ShowHideNextTurnGfx(TRUE); UpdateSliderHeartSpriteYPositions(); gTasks[taskId].data[0] = 1; break; case 1: if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) { u8 taskId2; eContest.waitForLink = TRUE; if (IsPlayerLinkLeader()) SetContestantStatusesForNextRound(); taskId2 = CreateTask(Task_LinkContest_CommunicateAppealsState, 0); SetTaskFuncWithFollowupFunc(taskId2, Task_LinkContest_CommunicateAppealsState, Task_EndWaitForLink); ContestPrintLinkStandby(); gTasks[taskId].data[0] = 2; } else { SetContestantStatusesForNextRound(); gTasks[taskId].data[0] = 3; } break; case 2: if (!eContest.waitForLink) gTasks[taskId].data[0] = 3; break; case 3: DrawStatusSymbols(); SwapMoveDescAndContestTilemaps(); gTasks[taskId].data[0] = 0; gTasks[taskId].func = Task_WaitRaiseCurtainAtRoundEnd; break; } } static void Task_UpdateRaiseCurtainAtRoundEnd(u8 taskId) { if ((s16)(gBattle_BG1_Y += 7) > DISPLAY_HEIGHT) gTasks[taskId].func = Task_UpdateContestantBoxOrder; } static void Task_WaitRaiseCurtainAtRoundEnd(u8 taskId) { if (gTasks[taskId].data[2] < 10) { gTasks[taskId].data[2]++; } else { if (gTasks[taskId].data[1] == 0) { if (gTasks[taskId].data[0] == 16) gTasks[taskId].data[1]++; else gTasks[taskId].data[0]++; } else { if (gTasks[taskId].data[0] == 0) { gTasks[taskId].data[1] = 0; gTasks[taskId].data[2] = 0; gTasks[taskId].func = Task_StartRaiseCurtainAtRoundEnd; } else { gTasks[taskId].data[0]--; } } } } static void Task_StartRaiseCurtainAtRoundEnd(u8 taskId) { if (gTasks[taskId].data[2] < 10) { gTasks[taskId].data[2]++; } else { gTasks[taskId].data[2] = 0; PlaySE12WithPanning(SE_CONTEST_CURTAIN_RISE, 0); gTasks[taskId].func = Task_UpdateRaiseCurtainAtRoundEnd; } } #define tAnimId data[0] static void AnimateSliderHearts(u8 animId) { s32 i; u8 taskId; for (i = 0; i < CONTESTANT_COUNT; i++) { gSprites[eContestGfxState[i].sliderHeartSpriteId].oam.matrixNum = AllocOamMatrix(); gSprites[eContestGfxState[i].sliderHeartSpriteId].oam.affineMode = ST_OAM_AFFINE_NORMAL; StartSpriteAffineAnim(&gSprites[eContestGfxState[i].sliderHeartSpriteId], animId); if (animId == SLIDER_HEART_ANIM_APPEAR) { AnimateSprite(&gSprites[eContestGfxState[i].sliderHeartSpriteId]); gSprites[eContestGfxState[i].sliderHeartSpriteId].invisible = FALSE; } } taskId = CreateTask(Task_WaitForSliderHeartAnim, 5); gTasks[taskId].tAnimId = animId; eContest.sliderHeartsAnimating = TRUE; } static void Task_WaitForSliderHeartAnim(u8 taskId) { s32 i; if (gSprites[eContestGfxState[0].sliderHeartSpriteId].affineAnimEnded) { if ((u8)gTasks[taskId].tAnimId == SLIDER_HEART_ANIM_DISAPPEAR) { for (i = 0; i < CONTESTANT_COUNT; i++) gSprites[eContestGfxState[i].sliderHeartSpriteId].invisible = TRUE; } for (i = 0; i < CONTESTANT_COUNT; i++) FreeSpriteOamMatrix(&gSprites[eContestGfxState[i].sliderHeartSpriteId]); eContest.sliderHeartsAnimating = FALSE; DestroyTask(taskId); } } #undef tAnimId static u16 SanitizeMove(u16 move) { if (move >= MOVES_COUNT) move = MOVE_POUND; return move; } static u16 SanitizeSpecies(u16 species) { if (species >= NUM_SPECIES) species = SPECIES_NONE; return species; } static void SetMoveSpecificAnimData(u8 contestant) { s32 i; u16 move = SanitizeMove(eContestantStatus[contestant].currMove); u16 species = SanitizeSpecies(gContestMons[contestant].species); u8 targetContestant; memset(&gContestResources->moveAnim->species, 0, 20); ClearBattleAnimationVars(); for (i = 0; i < CONTESTANT_COUNT; i++) gBattleMonForms[i] = 0; switch (move) { case MOVE_CURSE: if (gBaseStats[species].type1 == TYPE_GHOST || gBaseStats[species].type2 == TYPE_GHOST) gAnimMoveTurn = 0; else gAnimMoveTurn = 1; break; case MOVE_TRANSFORM: case MOVE_ROLE_PLAY: targetContestant = eContestantStatus[contestant].contestantAnimTarget; gContestResources->moveAnim->targetSpecies = SanitizeSpecies(gContestMons[targetContestant].species); gContestResources->moveAnim->targetPersonality = gContestMons[targetContestant].personality; gContestResources->moveAnim->hasTargetAnim = TRUE; break; case MOVE_RETURN: gAnimFriendship = MAX_FRIENDSHIP; break; case MOVE_FRUSTRATION: gAnimFriendship = 0; break; case MOVE_SOLAR_BEAM: case MOVE_RAZOR_WIND: case MOVE_SKULL_BASH: case MOVE_SKY_ATTACK: if (eContest.moveAnimTurnCount == 0) { eContest.moveAnimTurnCount = 2; gAnimMoveTurn = 0; } else { gAnimMoveTurn = 1; } break; } SetBattleTargetSpritePosition(); } static void ClearMoveAnimData(u8 contestant) { memset(gContestResources->moveAnim, 0, sizeof(struct ContestMoveAnimData)); if (eContest.moveAnimTurnCount != 0) eContest.moveAnimTurnCount--; } static void SetMoveAnimAttackerData(u8 contestant) { gContestResources->moveAnim->contestant = contestant; gContestResources->moveAnim->species = SanitizeSpecies(gContestMons[contestant].species); gContestResources->moveAnim->personality = gContestMons[contestant].personality; gContestResources->moveAnim->otId = gContestMons[contestant].otId; } static void CreateInvisibleBattleTargetSprite(void) { gBattlerSpriteIds[B_POSITION_OPPONENT_RIGHT] = CreateInvisibleSpriteWithCallback(SpriteCallbackDummy); InitSpriteAffineAnim(&gSprites[gBattlerSpriteIds[gBattlerTarget]]); SetBattleTargetSpritePosition(); } static void SetBattleTargetSpritePosition(void) { struct Sprite *sprite = &gSprites[gBattlerSpriteIds[B_POSITION_OPPONENT_RIGHT]]; sprite->x2 = 0; sprite->y2 = 0; sprite->x = GetBattlerSpriteCoord(B_POSITION_OPPONENT_RIGHT, BATTLER_COORD_X); sprite->y = GetBattlerSpriteCoord(B_POSITION_OPPONENT_RIGHT, BATTLER_COORD_Y); sprite->invisible = TRUE; } static void SetMoveTargetPosition(u16 move) { switch (gBattleMoves[move].target) { case MOVE_TARGET_USER_OR_SELECTED: case MOVE_TARGET_USER: gBattlerTarget = B_POSITION_PLAYER_RIGHT; break; case MOVE_TARGET_SELECTED: case MOVE_TARGET_RANDOM: case MOVE_TARGET_BOTH: case MOVE_TARGET_FOES_AND_ALLY: default: gBattlerTarget = B_POSITION_OPPONENT_RIGHT; break; } } static void Contest_PrintTextToBg0WindowStd(u32 windowId, const u8 *b) { struct TextPrinterTemplate printerTemplate; printerTemplate.currentChar = b; printerTemplate.windowId = windowId; printerTemplate.fontId = FONT_NORMAL; printerTemplate.x = 0; printerTemplate.y = 1; printerTemplate.currentX = 0; printerTemplate.currentY = 1; printerTemplate.letterSpacing = 0; printerTemplate.lineSpacing = 0; printerTemplate.unk = 0; printerTemplate.fgColor = 15; printerTemplate.bgColor = 0; printerTemplate.shadowColor = 8; AddTextPrinter(&printerTemplate, 0, 0); PutWindowTilemap(windowId); Contest_SetBgCopyFlags(0); } void Contest_PrintTextToBg0WindowAt(u32 windowId, u8 *currChar, s32 x, s32 y, s32 fontId) { struct TextPrinterTemplate printerTemplate; printerTemplate.currentChar = currChar; printerTemplate.windowId = windowId; printerTemplate.fontId = fontId; printerTemplate.x = x; printerTemplate.y = y; printerTemplate.currentX = x; printerTemplate.currentY = y; printerTemplate.letterSpacing = 0; printerTemplate.lineSpacing = 0; printerTemplate.unk = 0; printerTemplate.fgColor = 15; printerTemplate.bgColor = 0; printerTemplate.shadowColor = 8; AddTextPrinter(&printerTemplate, 0, 0); PutWindowTilemap(windowId); Contest_SetBgCopyFlags(0); } static void Contest_StartTextPrinter(const u8 *currChar, bool32 b) { struct TextPrinterTemplate printerTemplate; u8 speed; printerTemplate.currentChar = currChar; printerTemplate.windowId = 4; printerTemplate.fontId = FONT_NORMAL; printerTemplate.x = 0; printerTemplate.y = 1; printerTemplate.currentX = 0; printerTemplate.currentY = 1; printerTemplate.letterSpacing = 0; printerTemplate.lineSpacing = 0; printerTemplate.unk = 0; printerTemplate.fgColor = 1; printerTemplate.bgColor = 0; printerTemplate.shadowColor = 8; if (!b) { AddTextPrinter(&printerTemplate, 0, 0); } else { if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) speed = 4; else speed = GetPlayerTextSpeedDelay(); AddTextPrinter(&printerTemplate, speed, 0); } PutWindowTilemap(4); Contest_SetBgCopyFlags(0); } static void ContestBG_FillBoxWithIncrementingTile(u8 bg, u16 firstTileNum, u8 x, u8 y, u8 width, u8 height, u8 paletteSlot, s16 tileNumData) { WriteSequenceToBgTilemapBuffer(bg, firstTileNum, x, y, width, height, paletteSlot, tileNumData); Contest_SetBgCopyFlags(bg); } static void ContestBG_FillBoxWithTile(u8 bg, u16 firstTileNum, u8 x, u8 y, u8 width, u8 height, u8 paletteSlot) { ContestBG_FillBoxWithIncrementingTile(bg, firstTileNum, x, y, width, height, paletteSlot, 0); } static bool32 Contest_RunTextPrinters(void) { RunTextPrinters(); return IsTextPrinterActive(4); } static void Contest_SetBgCopyFlags(u32 flagIndex) { sContestBgCopyFlags |= 1 << flagIndex; } void ResetContestLinkResults(void) { s32 i; s32 j; for(i = 0; i < CONTEST_CATEGORIES_COUNT; i++) for(j = 0; j < CONTESTANT_COUNT; j++) gSaveBlock2Ptr->contestLinkResults[i][j] = 0; } bool8 SaveContestWinner(u8 rank) { s32 i; u8 captionId = Random() % NUM_PAINTING_CAPTIONS; // Get the index of the winner among the contestants for (i = 0; i < CONTESTANT_COUNT - 1; i++) if (gContestFinalStandings[i] == 0) break; // Exit if attempting to save a Pokémon other than the player's to the museum if (rank == CONTEST_SAVE_FOR_MUSEUM && i != gContestPlayerMonIndex) return FALSE; // Adjust the random painting caption depending on the category switch (gSpecialVar_ContestCategory) { case CONTEST_CATEGORY_COOL: captionId += NUM_PAINTING_CAPTIONS * CONTEST_CATEGORY_COOL; break; case CONTEST_CATEGORY_BEAUTY: captionId += NUM_PAINTING_CAPTIONS * CONTEST_CATEGORY_BEAUTY; break; case CONTEST_CATEGORY_CUTE: captionId += NUM_PAINTING_CAPTIONS * CONTEST_CATEGORY_CUTE; break; case CONTEST_CATEGORY_SMART: captionId += NUM_PAINTING_CAPTIONS * CONTEST_CATEGORY_SMART; break; case CONTEST_CATEGORY_TOUGH: captionId += NUM_PAINTING_CAPTIONS * CONTEST_CATEGORY_TOUGH; break; } if (rank != CONTEST_SAVE_FOR_ARTIST) { // Save winner in the saveblock // Used to save any winner for the Contest Hall or the Museum // but excludes the temporary save used by the artist u8 id = GetContestWinnerSaveIdx(rank, TRUE); gSaveBlock1Ptr->contestWinners[id].personality = gContestMons[i].personality; gSaveBlock1Ptr->contestWinners[id].species = gContestMons[i].species; gSaveBlock1Ptr->contestWinners[id].trainerId = gContestMons[i].otId; StringCopy(gSaveBlock1Ptr->contestWinners[id].monName, gContestMons[i].nickname); StringCopy(gSaveBlock1Ptr->contestWinners[id].trainerName, gContestMons[i].trainerName); if(gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) gSaveBlock1Ptr->contestWinners[id].contestRank = CONTEST_RANK_LINK; else gSaveBlock1Ptr->contestWinners[id].contestRank = gSpecialVar_ContestRank; if (rank != CONTEST_SAVE_FOR_MUSEUM) gSaveBlock1Ptr->contestWinners[id].contestCategory = gSpecialVar_ContestCategory; else gSaveBlock1Ptr->contestWinners[id].contestCategory = captionId; } else { // Set the most recent winner so the artist can show the player their painting gCurContestWinner.personality = gContestMons[i].personality; gCurContestWinner.trainerId = gContestMons[i].otId; gCurContestWinner.species = gContestMons[i].species; StringCopy(gCurContestWinner.monName, gContestMons[i].nickname); StringCopy(gCurContestWinner.trainerName, gContestMons[i].trainerName); gCurContestWinner.contestCategory = captionId; } return TRUE; } // Rank is either a regular contest rank (for saving winners to show in the Contest Hall) // Or one of two special IDs listed below (for saving winners to show in Museum, or from the artist) // If just retrieving the index where the winner *would* go, shift is FALSE // If actually preparing to insert the winner into the saveblock, shift is TRUE u8 GetContestWinnerSaveIdx(u8 rank, bool8 shift) { s32 i; switch (rank) { case CONTEST_RANK_NORMAL: case CONTEST_RANK_SUPER: case CONTEST_RANK_HYPER: case CONTEST_RANK_MASTER: if (shift) { for (i = NUM_CONTEST_HALL_WINNERS - 1; i > 0; i--) memcpy(&gSaveBlock1Ptr->contestWinners[i], &gSaveBlock1Ptr->contestWinners[i - 1], sizeof(struct ContestWinner)); } return CONTEST_WINNER_HALL_1 - 1; default: // case CONTEST_SAVE_FOR_MUSEUM: // case CONTEST_SAVE_FOR_ARTIST: switch (gSpecialVar_ContestCategory) { case CONTEST_CATEGORY_COOL: return CONTEST_WINNER_MUSEUM_COOL - 1; case CONTEST_CATEGORY_BEAUTY: return CONTEST_WINNER_MUSEUM_BEAUTY - 1; case CONTEST_CATEGORY_CUTE: return CONTEST_WINNER_MUSEUM_CUTE - 1; case CONTEST_CATEGORY_SMART: return CONTEST_WINNER_MUSEUM_SMART - 1; case CONTEST_CATEGORY_TOUGH: default: return CONTEST_WINNER_MUSEUM_TOUGH - 1; } } } void ClearContestWinnerPicsInContestHall(void) { s32 i; for (i = 0; i < MUSEUM_CONTEST_WINNERS_START; i++) gSaveBlock1Ptr->contestWinners[i] = gDefaultContestWinners[i]; } static void SetContestLiveUpdateFlags(u8 contestant) { s32 i; if (!eContestExcitement.frozen && eContestExcitement.moveExcitement > 0 && !eContestantStatus[contestant].repeatedMove) { gContestResources->tv[contestant].winnerFlags |= CONTESTLIVE_FLAG_EXCITING_APPEAL; gContestResources->tv[contestant].madeExcitingAppeal = TRUE; } if (eContestantStatus[contestant].nervous) gContestResources->tv[contestant].winnerFlags |= CONTESTLIVE_FLAG_GOT_NERVOUS; if (!eContestExcitement.frozen && eContestExcitement.moveExcitement != 0 && eContestExcitement.excitementAppealBonus == 60) { gContestResources->tv[contestant].winnerFlags |= CONTESTLIVE_FLAG_MAXED_EXCITEMENT; } if (eContestantStatus[contestant].usedComboMove && eContestantStatus[contestant].completedCombo) { gContestResources->tv[contestant].winnerFlags |= CONTESTLIVE_FLAG_USED_COMBO; } for (i = 0; i < CONTESTANT_COUNT; i++) { if (i != contestant && eContestantStatus[i].jam != 0) { gContestResources->tv[contestant].winnerFlags |= CONTESTLIVE_FLAG_STARTLED_OTHER; gContestResources->tv[i].winnerFlags |= CONTESTLIVE_FLAG_GOT_STARTLED; } } if (eContestantStatus[contestant].numTurnsSkipped != 0 || eContestantStatus[contestant].noMoreTurns) { gContestResources->tv[contestant].winnerFlags |= CONTESTLIVE_FLAG_SKIPPED_TURN; } else if (!eContestantStatus[contestant].nervous) { gContestResources->tv[contestant].winnerFlags |= CONTESTLIVE_FLAG_MADE_APPEAL; gContestResources->tv[contestant].madeAppeal = TRUE; gContestResources->tv[contestant].appeals[eContest.appealNumber] = eContestantStatus[contestant].currMove; } if (eContestantStatus[contestant].repeatedMove) gContestResources->tv[contestant].loserFlags |= CONTESTLIVE_FLAG_REPEATED_MOVE; if (eContest.applauseLevel == 4 && !eContestExcitement.frozen && eContestExcitement.moveExcitement < 0) { gContestResources->tv[contestant].loserFlags |= CONTESTLIVE_FLAG_MISSED_EXCITEMENT; } } static void CalculateContestLiveUpdateData(void) { u8 loser; s32 i, j; bool32 notLastInRound1, notLastInRound2; u16 appealMoves[CONTEST_NUM_APPEALS + 1]; u8 numMoveUses[CONTEST_NUM_APPEALS + 1]; u16 moveCandidates[CONTEST_NUM_APPEALS]; u8 winner; u8 mostUses; u8 numMoveCandidates; loser = 0; winner = 0; // Get loser/winner ids for (i = 0; i < CONTESTANT_COUNT; i++) { if (gContestFinalStandings[i] == 0) winner = i; else if (gContestFinalStandings[i] == CONTESTANT_COUNT - 1) loser = i; } // Set flags for commenting on loser gContestResources->tv[loser].loserFlags |= CONTESTLIVE_FLAG_LOST; for (i = 0; i < CONTESTANT_COUNT; i++) { if (i != winner && gContestMonTotalPoints[winner] - gContestMonTotalPoints[i] <= 50) gContestResources->tv[i].loserFlags |= CONTESTLIVE_FLAG_LOST_SMALL_MARGIN; if (!gContestResources->tv[i].madeExcitingAppeal) gContestResources->tv[i].loserFlags |= CONTESTLIVE_FLAG_NO_EXCITEMENT; for (j = 0; j < CONTESTANT_COUNT; j++) { if (gContestMonRound1Points[i] < gContestMonRound1Points[j]) break; } if (j == CONTESTANT_COUNT && gContestFinalStandings[i] != 0) gContestResources->tv[i].loserFlags |= CONTESTLIVE_FLAG_BLEW_LEAD; notLastInRound1 = FALSE; notLastInRound2 = FALSE; for (j = 0; j < CONTESTANT_COUNT; j++) { if (gContestMonRound1Points[i] > gContestMonRound1Points[j]) notLastInRound1 = TRUE; if (gContestMonRound2Points[i] > gContestMonRound2Points[j]) notLastInRound2 = TRUE; } if (!notLastInRound1 && !notLastInRound2) gContestResources->tv[i].loserFlags |= CONTESTLIVE_FLAG_LAST_BOTH_ROUNDS; if (!gContestResources->tv[i].madeAppeal) gContestResources->tv[i].loserFlags |= CONTESTLIVE_FLAG_NO_APPEALS; } // Get what moves the winner used and how many times they used them for (i = 0; i < CONTEST_NUM_APPEALS; i++) { appealMoves[i] = MOVE_NONE; numMoveUses[i] = 0; } appealMoves[CONTEST_NUM_APPEALS] = 0xFFFF; numMoveUses[CONTEST_NUM_APPEALS] = 0; for (i = 0; i < CONTEST_NUM_APPEALS; i++) { if (gContestResources->tv[winner].appeals[i] != MOVE_NONE) { for (j = 0; j < CONTEST_NUM_APPEALS; j++) { if (gContestResources->tv[winner].appeals[i] != appealMoves[j]) { if (appealMoves[j] == MOVE_NONE) { appealMoves[j] = gContestResources->tv[winner].appeals[i]; numMoveUses[j]++; } } else { numMoveUses[j]++; } } } } // Choose an appeal move to comment on for the winner (most commonly used) moveCandidates[0] = appealMoves[0]; mostUses = numMoveUses[0]; numMoveCandidates = 0; for (i = 1; appealMoves[i] != 0xFFFF; i++) { if (mostUses < numMoveUses[i]) { moveCandidates[0] = appealMoves[i]; mostUses = numMoveUses[i]; numMoveCandidates = 1; } else if (mostUses == numMoveUses[i]) { moveCandidates[numMoveCandidates] = appealMoves[i]; numMoveCandidates++; } } gContestResources->tv[winner].move = moveCandidates[Random() % numMoveCandidates]; } static void SetConestLiveUpdateTVData(void) { s32 i; u32 flags; u8 winner; u8 round1Placing, round2Placing; u8 count; u8 randAction; u8 numLoserCandidates; u8 flagId; u16 winnerFlag; u8 loserFlag; u8 loser; u8 loserCandidates[CONTESTANT_COUNT - 1]; // Players mon didn't win, don't generate show if (gContestFinalStandings[gContestPlayerMonIndex] != 0) return; // Get winner id (unnecessary, we now know it's gContestPlayerMonIndex) winner = 0; for (i = 0; i < CONTESTANT_COUNT; i++) { if (gContestFinalStandings[i] == 0) winner = i; } // Get winner's placement in Round 1 and 2 round1Placing = 0; round2Placing = 0; for (i = 0; i < CONTESTANT_COUNT; i++) { if (gContestMonRound1Points[winner] < gContestMonRound1Points[i]) round1Placing++; if (gContestMonRound2Points[winner] < gContestMonRound2Points[i]) round2Placing++; } // Count how many TV comment-worthy actions the winner took flags = gContestResources->tv[winner].winnerFlags; count = 0; for (i = 0; i < 8; flags >>= 1, i++) { if (flags & 1) count++; } // Randomly choose one of these actions to comment on randAction = Random() % count; flags = gContestResources->tv[winner].winnerFlags; count = 0; flagId = 0; for (i = 0; i < 8; flags >>= 1, flagId++, i++) { if (!(flags & 1)) continue; if (randAction == count) break; count++; } winnerFlag = 1 << flagId; // Pick a losing player with the highest severity of bad actions to comment on if (winner == 0) { loserCandidates[0] = 1; loserFlag = gContestResources->tv[1].loserFlags; i = 2; } else { loserCandidates[0] = 0; loserFlag = gContestResources->tv[0].loserFlags; i = 1; } numLoserCandidates = 1; for (; i < CONTESTANT_COUNT; i++) { if (i != winner) { if (loserFlag < gContestResources->tv[i].loserFlags) { // Losing player currently has the worst (highest) set of flags, only candidate loserCandidates[0] = i; loserFlag = gContestResources->tv[i].loserFlags; numLoserCandidates = 1; } else if (loserFlag == gContestResources->tv[i].loserFlags) { // Tie, increment number of loser candidates loserCandidates[numLoserCandidates] = i; numLoserCandidates++; } } } loser = loserCandidates[Random() % numLoserCandidates]; // Choose the "worst" action to comment on (flag with highest value) flagId = CONTESTLIVE_FLAG_NO_APPEALS; for (i = 0; i < 8; flagId >>= 1, i++) { loserFlag = gContestResources->tv[loser].loserFlags & flagId; if (loserFlag) break; } ContestLiveUpdates_Init(round1Placing); ContestLiveUpdates_SetRound2Placing(round2Placing); ContestLiveUpdates_SetWinnerAppealFlag(winnerFlag); ContestLiveUpdates_SetWinnerMoveUsed(gContestResources->tv[winner].move); ContestLiveUpdates_SetLoserData(loserFlag, loser); } // Unused void ContestDebugToggleBitfields(bool8 loserFlags) { if (eContestDebugMode == CONTEST_DEBUG_MODE_OFF) { if (!loserFlags) eContestDebugMode = CONTEST_DEBUG_MODE_PRINT_WINNER_FLAGS; else eContestDebugMode = CONTEST_DEBUG_MODE_PRINT_LOSER_FLAGS; } else { eContestDebugMode = CONTEST_DEBUG_MODE_OFF; } if (eContestDebugMode == CONTEST_DEBUG_MODE_OFF) { DrawContestantWindowText(); SwapMoveDescAndContestTilemaps(); } else { ContestDebugPrintBitStrings(); } } static void ContestDebugPrintBitStrings(void) { u8 i; s8 j; u8 text1[20]; u8 text2[20]; u8 *txtPtr; u32 bits; if (!gEnableContestDebugging) return; if (eContestDebugMode != CONTEST_DEBUG_MODE_PRINT_WINNER_FLAGS && eContestDebugMode != CONTEST_DEBUG_MODE_PRINT_LOSER_FLAGS) return; for (i = 0; i < CONTESTANT_COUNT; i++) FillWindowPixelBuffer(i, PIXEL_FILL(0)); if (eContestDebugMode == CONTEST_DEBUG_MODE_PRINT_WINNER_FLAGS) { for (i = 0; i < CONTESTANT_COUNT; i++) { txtPtr = StringCopy(text1, gText_CDot); Contest_PrintTextToBg0WindowAt(gContestantTurnOrder[i], text1, 5, 1, FONT_NARROW); bits = gContestResources->tv[i].winnerFlags; for (j = 7; j > -1; j--) // Weird loop. { txtPtr = ConvertIntToDecimalStringN(txtPtr, bits & 1, STR_CONV_MODE_LEFT_ALIGN, 1); bits >>= 1; } for (j = 0; j < 5; j++) text2[j] = text1[j]; text2[j] = EOS; Contest_PrintTextToBg0WindowAt(gContestantTurnOrder[i], text2, 5, 1, 7); Contest_PrintTextToBg0WindowAt(gContestantTurnOrder[i], text1 + j, 55, 1, FONT_NARROW); } } else // CONTEST_DEBUG_MODE_PRINT_LOSER_FLAGS { for (i = 0; i < CONTESTANT_COUNT; i++) { StringCopy(text1, gText_BDot); bits = gContestResources->tv[i].loserFlags; txtPtr = &text1[2]; for (j = 7; j > -1; j--) // Weird loop. { txtPtr = ConvertIntToDecimalStringN(txtPtr, bits & 1, STR_CONV_MODE_LEFT_ALIGN, 1); bits >>= 1; } for (j = 0; j < 5; j++) text2[j] = text1[j]; text2[j] = EOS; Contest_PrintTextToBg0WindowAt(gContestantTurnOrder[i], text2, 5, 1, FONT_NARROW); Contest_PrintTextToBg0WindowAt(gContestantTurnOrder[i], text1 + j, 55, 1, FONT_NARROW); } } SwapMoveDescAndContestTilemaps(); } static u8 GetMonNicknameLanguage(u8 *nickname) { u8 ret = GAME_LANGUAGE; if (nickname[0] == EXT_CTRL_CODE_BEGIN && nickname[1] == EXT_CTRL_CODE_JPN) return GAME_LANGUAGE; if (StringLength(nickname) < PLAYER_NAME_LENGTH - 1) { while (*nickname != EOS) { if ((*nickname >= CHAR_A && *nickname <= CHAR_z) || (*nickname >= CHAR_0 && *nickname <= CHAR_9) || *nickname == CHAR_SPACE || *nickname == CHAR_PERIOD || *nickname == CHAR_COMMA || *nickname == CHAR_EXCL_MARK || *nickname == CHAR_QUESTION_MARK || *nickname == CHAR_MALE || *nickname == CHAR_FEMALE || *nickname == CHAR_SLASH || *nickname == CHAR_HYPHEN || *nickname == CHAR_ELLIPSIS || *nickname == CHAR_DBL_QUOTE_LEFT || *nickname == CHAR_DBL_QUOTE_RIGHT || *nickname == CHAR_SGL_QUOTE_LEFT || *nickname == CHAR_DBL_QUOTE_LEFT) // Most likely a typo, CHAR_SGL_QUOTE_RIGHT should be here instead. { nickname++; } else { ret = LANGUAGE_JAPANESE; break; } } } return ret; } static void StripPlayerNameForLinkContest(u8 *playerName) { u8 chr = playerName[5]; playerName[5] = EOS; playerName[PLAYER_NAME_LENGTH] = chr; } static void StripMonNameForLinkContest(u8 *monName, s32 language) { u8 chr; StripExtCtrlCodes(monName); if (language == LANGUAGE_JAPANESE) { monName[5] = EOS; monName[POKEMON_NAME_LENGTH] = EXT_CTRL_CODE_BEGIN; } else { chr = monName[5]; monName[5] = EOS; monName[POKEMON_NAME_LENGTH] = chr; } } void StripPlayerAndMonNamesForLinkContest(struct ContestPokemon *mon, s32 language) { u8 *name = mon->nickname; if (language == LANGUAGE_JAPANESE) { ConvertInternationalString(name, GetMonNicknameLanguage(name)); } else if (name[POKEMON_NAME_LENGTH] == EXT_CTRL_CODE_BEGIN) { ConvertInternationalString(name, LANGUAGE_JAPANESE); } else { name[5] = name[POKEMON_NAME_LENGTH]; name[POKEMON_NAME_LENGTH] = EOS; } name = mon->trainerName; if (language == LANGUAGE_JAPANESE) { name[PLAYER_NAME_LENGTH] = EOS; name[6] = name[4]; name[5] = name[3]; name[4] = name[2]; name[3] = name[1]; name[2] = mon->trainerName[0]; name[1] = EXT_CTRL_CODE_JPN; name[0] = EXT_CTRL_CODE_BEGIN; } else { name[5] = name[PLAYER_NAME_LENGTH]; name[PLAYER_NAME_LENGTH] = EOS; } }