pokeemerald/src/dodrio_berry_picking.c

5222 lines
152 KiB
C
Raw Normal View History

2019-03-24 21:42:42 +01:00
#include "global.h"
#include "malloc.h"
2019-03-25 17:08:56 +01:00
#include "bg.h"
2019-08-02 00:41:55 +02:00
#include "dodrio_berry_picking.h"
#include "dynamic_placeholder_text_util.h"
2019-08-01 19:01:50 +02:00
#include "event_data.h"
2019-08-02 00:41:55 +02:00
#include "gpu_regs.h"
#include "international_string_util.h"
2019-08-01 19:01:50 +02:00
#include "item.h"
2019-03-24 21:42:42 +01:00
#include "link.h"
2019-03-24 22:32:59 +01:00
#include "link_rfu.h"
2019-08-02 00:41:55 +02:00
#include "m4a.h"
#include "main.h"
#include "palette.h"
2020-04-08 23:23:32 +02:00
#include "minigame_countdown.h"
2019-08-02 00:41:55 +02:00
#include "random.h"
#include "save.h"
2019-08-01 19:01:50 +02:00
#include "script.h"
2019-08-02 00:41:55 +02:00
#include "sound.h"
2019-08-01 19:01:50 +02:00
#include "string_util.h"
2019-08-02 00:41:55 +02:00
#include "strings.h"
#include "task.h"
#include "text_window.h"
#include "window.h"
2019-08-01 19:01:50 +02:00
#include "constants/items.h"
2019-08-02 00:41:55 +02:00
#include "constants/songs.h"
2019-03-24 21:42:42 +01:00
2021-03-01 07:54:51 +01:00
// Note that in this file 'Dodrio Berry Picking' is often
// shortened to DodrioGame or just Game for convenience
#define MAX_SCORE 999990
#define MAX_BERRIES 9999
// The minimum score needed to receive a prize
#define PRIZE_SCORE 3000
// Difficulty increases as berries are eaten. The rate of new berries increases and the types of berries changes
// When the max difficulty is reached it starts again from the beginning
#define NUM_DIFFICULTIES 7
#define MAX_FALL_DIST 10 // The number of times a berry needs to fall before hitting the ground
#define EAT_FALL_DIST 7 // The number of times a berry needs to fall to be available to eat
enum {
BG_INTERFACE,
BG_TREE_LEFT,
BG_TREE_RIGHT,
BG_SCENERY
};
enum {
FUNC_INTRO,
FUNC_INIT_COUNTDOWN,
FUNC_COUNTDOWN,
FUNC_WAIT_START,
FUNC_PLAY_GAME,
FUNC_INIT_RESULTS,
FUNC_RESULTS,
FUNC_ASK_PLAY_AGAIN,
FUNC_END_LINK,
FUNC_EXIT,
FUNC_RESET_GAME,
FUNC_WAIT_END_GAME,
};
enum {
GFXFUNC_LOAD,
GFXFUNC_SHOW_NAMES,
GFXFUNC_SHOW_RESULTS,
GFXFUNC_MSG_PLAY_AGAIN,
GFXFUNC_MSG_SAVING,
GFXFUNC_MSG_COMM_STANDBY,
GFXFUNC_ERASE_MSG,
GFXFUNC_MSG_PLAYER_DROPPED,
GFXFUNC_STOP,
GFXFUNC_IDLE,
};
enum {
PACKET_READY_START = 1,
PACKET_GAME_STATE,
PACKET_PICK_STATE,
PACKET_READY_END,
};
enum {
PLAY_AGAIN_NONE,
PLAY_AGAIN_YES,
PLAY_AGAIN_NO,
PLAY_AGAIN_DROPPED = 5,
};
enum {
PICK_NONE, // Dodrio standing still
PICK_RIGHT, // Dodrio reaching right
PICK_MIDDLE, // Dodrio reaching up
PICK_LEFT, // Dodrio reaching left
PICK_DISABLED, // Dodrio down after game over
};
enum {
BERRY_BLUE,
BERRY_GREEN,
BERRY_GOLD,
BERRY_MISSED,
BERRY_PRIZE,
BERRY_IN_ROW,
NUM_BERRY_IDS
};
#define NUM_BERRY_TYPES 4 // Blue, Green, Gold, and 'missed'
// Eaten anim comes after the normal & missed versions of other berries
#define ANIM_EATEN (BERRY_MISSED * 2)
enum {
BERRYSTATE_NONE,
BERRYSTATE_PICKED, // Berry has been picked by a Dodrio, replaced with blue hit sprite (still falling)
BERRYSTATE_EATEN, // Berry has been eaten (after being picked), berry is gone now
BERRYSTATE_SQUISHED, // Berry has hit the ground
};
enum {
INPUTSTATE_NONE,
INPUTSTATE_TRY_PICK,
INPUTSTATE_PICKED,
INPUTSTATE_ATE_BERRY,
INPUTSTATE_BAD_MISS,
};
// Colors for status bar squares
// Colored gray when a berry is missed
// Flash red when few yellow squares remain
enum {
STATUS_YELLOW,
STATUS_GRAY,
STATUS_RED,
};
#define NUM_STATUS_SQUARES 10
// Berries fall in predefined columns.
2021-03-01 07:54:51 +01:00
// A total of 10 are available, though fewer will be used with < 5 players
// The 11th column is a repeat of the 1st column wrapped around, so only
// the values 0-9 are unique 'valid' columns
#define NUM_BERRY_COLUMNS 11
2021-03-11 20:16:40 +01:00
#define GFXTAG_DODRIO 0
#define GFXTAG_STATUS 1
#define GFXTAG_BERRIES 2
#define GFXTAG_CLOUD 5
#define GFXTAG_COUNTDOWN 7
2021-03-01 07:54:51 +01:00
#define PALTAG_DODRIO_NORMAL 0
#define PALTAG_DODRIO_SHINY 1
#define PALTAG_STATUS 2
#define PALTAG_BERRIES 3
#define PALTAG_CLOUD 6
2021-03-11 20:16:40 +01:00
#define PALTAG_COUNTDOWN 8
2021-03-01 07:54:51 +01:00
#define NUM_CLOUDS 2
#define PLAYER_NONE 0xFF
struct DodrioGame_Gfx
{
u16 ALIGNED(4) tilemapBuffers[3][BG_SCREEN_SIZE];
bool32 finished;
u8 ALIGNED(4) taskId;
u8 ALIGNED(4) windowIds[MAX_RFU_PLAYERS + 5]; // The latter 5 are never used
u8 ALIGNED(4) state;
u8 ALIGNED(4) loadState;
u16 ALIGNED(4) timer;
u8 ALIGNED(4) cursorSelection;
u8 ALIGNED(4) playAgainState;
void (*func)(void);
2019-03-24 22:32:59 +01:00
}; // size = 0x302C
2021-03-01 07:54:51 +01:00
struct StatusBar
2019-03-24 22:32:59 +01:00
{
2021-03-01 07:54:51 +01:00
u8 unused[12];
bool8 entered[NUM_STATUS_SQUARES];
s16 yChange[NUM_STATUS_SQUARES];
u16 spriteIds[NUM_STATUS_SQUARES];
u16 flashTimer;
2019-03-24 22:32:59 +01:00
}; // size = 0x40
2019-03-24 21:42:42 +01:00
2021-03-01 07:54:51 +01:00
struct DodrioGame_Berries
2019-03-26 16:59:39 +01:00
{
2021-03-01 07:54:51 +01:00
u8 ids[NUM_BERRY_COLUMNS];
u8 fallDist[NUM_BERRY_COLUMNS];
2019-03-26 16:59:39 +01:00
};
2021-03-01 07:54:51 +01:00
struct DodrioGame_PlayerCommData
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 pickState;
bool8 ALIGNED(4) ateBerry;
bool8 ALIGNED(4) missedBerry;
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
struct DodrioGame_Player
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 name[16];
bool32 receivedGameStatePacket; // Never read
struct DodrioGame_Berries berries;
struct DodrioGame_PlayerCommData comm;
u32 unused;
2019-03-24 21:42:42 +01:00
}; // size = 0x3C
2021-03-01 07:54:51 +01:00
// Because Dodrio is required for this minigame,
// the only relevant information about the selected
// Pokémon is whether or not it's shiny
struct DodrioGame_MonInfo
2019-03-24 21:42:42 +01:00
{
bool8 isShiny;
};
2021-03-01 07:54:51 +01:00
struct DodrioGame_ScoreResults
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 ranking;
u32 score;
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
struct DodrioGame
{
/*0x0000*/ void (*exitCallback)(void);
/*0x0004*/ u8 ALIGNED(4) taskId;
/*0x0008*/ u8 ALIGNED(4) playersReceived;
/*0x000C*/ u8 ALIGNED(4) startState;
/*0x0010*/ u8 ALIGNED(4) state;
/*0x0014*/ u8 ALIGNED(4) timer;
/*0x0018*/ u8 ALIGNED(4) funcId;
/*0x001C*/ u8 ALIGNED(4) prevFuncId; // Set, never read
/*0x0020*/ bool8 ALIGNED(4) isLeader;
/*0x0024*/ u8 ALIGNED(4) numPlayers;
2019-08-02 00:41:55 +02:00
/*0x0028*/ u8 ALIGNED(4) multiplayerId;
2021-03-01 07:54:51 +01:00
/*0x0029*/ u8 unused1[7];
/*0x0030*/ u8 ALIGNED(4) countdownEndDelay;
/*0x0034*/ u8 ALIGNED(4) posToPlayerId[MAX_RFU_PLAYERS];
/*0x003C*/ u8 ALIGNED(4) unused2; // Set to 0, never read
/*0x0040*/ u8 ALIGNED(4) numGraySquares;
/*0x0044*/ u8 ALIGNED(4) berryColStart;
/*0x0048*/ u8 ALIGNED(4) berryColEnd;
/*0x004A*/ u16 berryResults[MAX_RFU_PLAYERS][NUM_BERRY_IDS];
/*0x0086*/ u16 berriesEaten[MAX_RFU_PLAYERS];
/*0x0090*/ u8 ALIGNED(4) difficulty[MAX_RFU_PLAYERS];
/*0x0098*/ u8 ALIGNED(4) pickStateQueue[4];
/*0x009C*/ u8 ALIGNED(4) eatTimer[NUM_BERRY_COLUMNS];
/*0x00A8*/ u8 ALIGNED(4) inputState[MAX_RFU_PLAYERS];
/*0x00B0*/ u8 ALIGNED(4) inputDelay[MAX_RFU_PLAYERS];
/*0x00B8*/ u8 ALIGNED(4) berryEatenBy[NUM_BERRY_COLUMNS];
/*0x00C4*/ u8 ALIGNED(4) berryState[NUM_BERRY_COLUMNS];
/*0x00D0*/ u8 ALIGNED(4) fallTimer[NUM_BERRY_COLUMNS];
/*0x00DC*/ u8 ALIGNED(4) newBerryTimer[NUM_BERRY_COLUMNS];
/*0x00E8*/ u8 ALIGNED(4) prevBerryIds[NUM_BERRY_COLUMNS];
/*0x00F4*/ u8 ALIGNED(4) playersAttemptingPick[NUM_BERRY_COLUMNS][2];
/*0x010C*/ u8 ALIGNED(4) playAgainStates[MAX_RFU_PLAYERS];
/*0x0112*/ u16 berriesPickedInRow;
/*0x0114*/ u16 maxBerriesPickedInRow;
/*0x0118*/ bool32 startCountdown; // Never read
/*0x011C*/ bool32 startGame;
/*0x0120*/ bool32 berriesFalling;
/*0x0124*/ u8 ALIGNED(4) clearRecvCmdTimer;
/*0x0128*/ bool8 ALIGNED(4) clearRecvCmds;
/*0x012C*/ bool32 allReadyToEnd;
/*0x0130*/ bool32 readyToEnd[MAX_RFU_PLAYERS];
/*0x0144*/ bool8 ALIGNED(4) playingPickSound;
/*0x0148*/ bool8 ALIGNED(4) playingSquishSound[NUM_BERRY_COLUMNS];
/*0x0154*/ u8 ALIGNED(4) endSoundState;
/*0x0158*/ bool8 ALIGNED(4) readyToStart[MAX_RFU_PLAYERS];
/*0x0160*/ struct DodrioGame_Gfx gfx;
/*0x318C*/ struct DodrioGame_MonInfo monInfo[MAX_RFU_PLAYERS];
/*0x31A0*/ struct DodrioGame_Player players[MAX_RFU_PLAYERS];
/*0x32CC*/ struct DodrioGame_Player player;
/*0x3308*/ struct DodrioGame_ScoreResults scoreResults[MAX_RFU_PLAYERS];
2019-03-24 21:42:42 +01:00
}; // size = 0x3330
2021-03-01 07:54:51 +01:00
EWRAM_DATA static struct DodrioGame * sGame = NULL;
EWRAM_DATA static u16 * sDodrioSpriteIds[MAX_RFU_PLAYERS] = {NULL};
EWRAM_DATA static u16 * sCloudSpriteIds[NUM_CLOUDS] = {NULL};
EWRAM_DATA static u16 * sBerrySpriteIds[NUM_BERRY_COLUMNS] = {NULL};
EWRAM_DATA static u16 * sBerryIconSpriteIds[NUM_BERRY_TYPES] = {NULL};
EWRAM_DATA static struct StatusBar * sStatusBar = NULL;
EWRAM_DATA static struct DodrioGame_Gfx * sGfx = NULL;
static bool32 sExitingGame;
static void ResetTasksAndSprites(void);
static void InitDodrioGame(struct DodrioGame *);
static void Task_StartDodrioGame(u8);
static void DoGameIntro(void);
static void InitCountdown(void);
static void DoCountdown(void);
static void WaitGameStart(void);
static void PlayGame_Leader(void);
static void PlayGame_Member(void);
static void WaitEndGame_Leader(void);
static void WaitEndGame_Member(void);
static void InitResults_Leader(void);
static void InitResults_Member(void);
static void DoResults(void);
static void AskPlayAgain(void);
static void EndLink(void);
static void ExitGame(void);
static void ResetGame(void);
static void Task_NewGameIntro(u8);
static void Task_CommunicateMonInfo(u8);
static void RecvLinkData_Leader(void);
static void SendLinkData_Leader(void);
static void RecvLinkData_Member(void);
static void SendLinkData_Member(void);
static void HandleSound_Leader(void);
static void HandleSound_Member(void);
static void CB2_DodrioGame(void);
static void VBlankCB_DodrioGame(void);
static void InitMonInfo(struct DodrioGame_MonInfo *, struct Pokemon *);
static void CreateTask_(TaskFunc, u8);
static void CreateDodrioGameTask(TaskFunc);
static void SetGameFunc(u8);
static bool32 SlideTreeBordersOut(void);
static void InitFirstWaveOfBerries(void);
static bool32 TryPickBerry(u8, u8, u8);
static void UpdateFallingBerries(void);
static void UpdateGame_Leader(void);
static void UpdateGame_Member(void);
static void GetActiveBerryColumns(u8, u8 *, u8 *);
2021-03-01 07:54:51 +01:00
static bool32 AllPlayersReadyToStart(void);
static void ResetReadyToStart(void);
static bool32 ReadyToEndGame_Leader(void);
static bool32 ReadyToEndGame_Member(void);
static void TryIncrementDifficulty(u8);
static u8 GetPlayerIdAtColumn(u8);
static u8 GetNewBerryId(u8, u8);
static void IncrementBerryResult(u8, u8, u8);
static void UpdateBerriesPickedInRow(bool32);
static void SetMaxBerriesPickedInRow(void);
static void ResetForPlayAgainPrompt(void);
static void SetRandomPrize(void);
static void TryUpdateRecords(void);
static u8 UpdatePickStateQueue(u8);
static void HandleWaitPlayAgainInput(void);
static void ResetPickState(void);
static u32 GetHighestScore(void);
static void SendPacket_ReadyToStart(bool32);
static void SendPacket_GameState(struct DodrioGame_Player *,
struct DodrioGame_PlayerCommData *,
struct DodrioGame_PlayerCommData *,
struct DodrioGame_PlayerCommData *,
struct DodrioGame_PlayerCommData *,
struct DodrioGame_PlayerCommData *,
2021-03-01 07:54:51 +01:00
u8 , bool32 , bool32 );
static bool32 RecvPacket_GameState(u32,
struct DodrioGame_Player *,
struct DodrioGame_PlayerCommData *,
struct DodrioGame_PlayerCommData *,
struct DodrioGame_PlayerCommData *,
struct DodrioGame_PlayerCommData *,
struct DodrioGame_PlayerCommData *,
2021-03-01 07:54:51 +01:00
u8 *, bool32 *, bool32 *);
static void SendPacket_PickState(u8);
static bool32 RecvPacket_PickState(u32, u8 *);
static void SendPacket_ReadyToEnd(bool32);
static bool32 RecvPacket_ReadyToEnd(u32);
static void LoadDodrioGfx(void);
static void CreateDodrioSprite(struct DodrioGame_MonInfo *, u8, u8, u8);
static void StartDodrioMissedAnim(u8);
static void StartDodrioIntroAnim(u8);
static void FreeDodrioSprites(u8);
static void SetAllDodrioInvisibility(bool8, u8);
static void CreateStatusBarSprites(void);
static void FreeStatusBar(void);
static void SetStatusBarInvisibility(bool8);
static void InitStatusBarPos(void);
static bool32 DoStatusBarIntro(void);
static void LoadBerryGfx(void);
static void CreateBerrySprites(void);
static void FreeBerrySprites(void);
static void CreateCloudSprites(void);
static void ResetCloudPos(void);
static void StartCloudMovement(void);
static void FreeCloudSprites(void);
static void SetCloudInvisibility(bool8);
static void ResetBerryAndStatusBarSprites(void);
static void ResetGfxState(void);
static void InitGameGfx(struct DodrioGame_Gfx *);
static void SetGfxFuncById(u8);
static bool32 IsGfxFuncActive(void);
static u8 GetPlayAgainState(void);
static void SetBerryInvisibility(u8, bool8);
static void SetBerryIconsInvisibility(bool8);
static void SetBerryAnim(u16, u8);
static void SetBerryYPos(u8, u8);
static void SetDodrioAnim(u8, u8);
static u8 GetNewBerryIdByDifficulty(u8, u8);
static void UpdateStatusBarAnim(u8);
static u32 RecvPacket_ReadyToStart(u32);
static u32 IncrementWithLimit(u32, u32);
static u32 Min(u32, u32);
static u32 GetScore(u8);
static void Task_ShowDodrioBerryPickingRecords(u8);
static void Task_TryRunGfxFunc(u8);
static void PrintRecordsText(u8, s32);
static void SpriteCB_Status(struct Sprite *);
static void SpriteCB_Dodrio(struct Sprite *);
static u32 DoDodrioMissedAnim(struct Sprite *);
static u32 DoDodrioIntroAnim(struct Sprite *);
static s16 GetDodrioXPos(u8, u8);
static void SetDodrioInvisibility(bool8, u8);
static void LoadGfx(void);
static bool32 LoadBgGfx(void);
static void InitBgs(void);
static void SetGfxFunc(void (*func)(void));
static void (*GetGfxFunc(void))(void);
static void ShowNames(void);
static void ShowResults(void);
static void Msg_WantToPlayAgain(void);
static void Msg_SavingDontTurnOff(void);
static void Msg_CommunicationStandby(void);
static void EraseMessage(void);
static void Msg_SomeoneDroppedOut(void);
static void StopGfxFuncs(void);
static void GfxIdle(void);
// For each player, the array is a list of all the columns starting with the column to their left
// Only the range of active columns is read from the array (dependent on the number of players),
2021-03-01 07:54:51 +01:00
// so the arrays are spaced such that the numbers in the center are where the data that's read starts and end.
static const u8 sActiveColumnMap[MAX_RFU_PLAYERS][MAX_RFU_PLAYERS][NUM_BERRY_COLUMNS] =
{
{ // 1 player (never used), columns 4-6.
2021-03-01 07:54:51 +01:00
// Sometimes read to get default order regardless of the current number of players
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
2019-08-02 00:49:31 +02:00
},
2021-03-01 07:54:51 +01:00
{ // 2 players (never used), columns 3-6
{0, 1, 2, 3, 4, 5, 6, 3, 8, 9, 0},
{0, 1, 2, 5, 6, 3, 4, 5, 8, 9, 0},
2019-08-02 00:49:31 +02:00
},
2021-03-01 07:54:51 +01:00
{ // 3 players, columns 2-7
{0, 1, 2, 3, 4, 5, 6, 7, 2, 9, 0},
{0, 1, 4, 5, 6, 7, 2, 3, 4, 9, 0},
{0, 1, 6, 7, 2, 3, 4, 5, 6, 9, 0},
2019-08-02 00:49:31 +02:00
},
2021-03-01 07:54:51 +01:00
{ // 4 players, columns 1-8
{0, 1, 2, 3, 4, 5, 6, 7, 8, 1, 0},
{0, 3, 4, 5, 6, 7, 8, 1, 2, 3, 0},
{0, 5, 6, 7, 8, 1, 2, 3, 4, 5, 0},
{0, 7, 8, 1, 2, 3, 4, 5, 6, 7, 0},
2019-08-02 00:49:31 +02:00
},
2021-03-01 07:54:51 +01:00
{ // 5 players, all columns (0-9)
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 },
{ 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2 },
{ 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4 },
{ 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 },
{ 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8 },
2019-08-02 00:49:31 +02:00
},
2019-07-31 00:27:30 +02:00
};
2021-03-01 07:54:51 +01:00
// A table for which falling berry column corresponds to which Dodrio head for each player
// The numbers in each array are the column number for each head, {left, middle, right}
// Dependent on the number of players
static const u8 sDodrioHeadToColumnMap[MAX_RFU_PLAYERS][MAX_RFU_PLAYERS][3] =
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
{ // 1 player (never used)
2019-08-02 00:49:31 +02:00
{4, 5, 6},
},
2021-03-01 07:54:51 +01:00
{ // 2 players (never used)
2019-08-02 00:49:31 +02:00
{3, 4, 5},
{5, 6, 3},
},
2021-03-01 07:54:51 +01:00
{ // 3 players
2019-08-02 00:49:31 +02:00
{4, 5, 6},
{6, 7, 2},
{2, 3, 4},
},
2021-03-01 07:54:51 +01:00
{ // 4 players
2019-08-02 00:49:31 +02:00
{3, 4, 5},
{5, 6, 7},
{7, 8, 1},
{1, 2, 3},
},
2021-03-01 07:54:51 +01:00
{ // 5 players
2019-08-02 00:49:31 +02:00
{4, 5, 6},
{6, 7, 8},
{8, 9, 0},
{0, 1, 2},
{2, 3, 4},
},
2019-07-31 00:27:30 +02:00
};
2021-03-01 07:54:51 +01:00
// A table of player ids and their neighbor, dependent on the total number of players
// {L, M, R}, where M is the player in question, L is their neighbor to the left, and R is their neighbor to the right
static const u8 sDodrioNeighborMap[MAX_RFU_PLAYERS][MAX_RFU_PLAYERS][3] =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
{ // 1 player (never used)
2019-08-02 00:49:31 +02:00
{1, 0, 1},
},
2021-03-01 07:54:51 +01:00
{ // 2 players (never used)
2019-08-02 00:49:31 +02:00
{1, 0, 1},
{0, 1, 0},
},
2021-03-01 07:54:51 +01:00
{ // 3 players
2019-08-02 00:49:31 +02:00
{2, 0, 1},
{0, 1, 2},
{1, 2, 0},
},
2021-03-01 07:54:51 +01:00
{ // 4 players
2019-08-02 00:49:31 +02:00
{3, 0, 1},
{0, 1, 2},
{1, 2, 3},
{2, 3, 0},
},
2021-03-01 07:54:51 +01:00
{ // 5 players
2019-08-02 00:49:31 +02:00
{4, 0, 1},
{0, 1, 2},
{1, 2, 3},
{2, 3, 4},
{3, 4, 0},
},
2019-08-01 19:01:50 +02:00
};
2021-10-05 14:54:15 +02:00
#define x 9 // No player at this column. This may go out of bounds if this is returned
2021-03-01 07:54:51 +01:00
// Takes the number of players and a column and returns the player id at that column.
// Note that the assignment is somewhat arbitrary as players share neighboring columns.
2019-08-01 19:01:50 +02:00
ALIGNED(4)
2021-03-01 07:54:51 +01:00
static const u8 sPlayerIdAtColumn[MAX_RFU_PLAYERS][NUM_BERRY_COLUMNS] =
2019-08-01 19:01:50 +02:00
{
2021-10-05 14:54:15 +02:00
{x, x, x, x, 1, 1, 1, x, x, x, x}, // 1 player
{x, x, x, 0, 0, 1, 1, 0, x, x, x}, // 2 players
{x, x, 2, 2, 0, 0, 1, 1, 1, x, x}, // 3 players
{x, 3, 3, 0, 0, 1, 1, 2, 2, 3, x}, // 4 players
{3, 3, 4, 4, 0, 0, 1, 1, 2, 2, 3}, // 5 players
2019-08-01 19:01:50 +02:00
};
2021-10-05 14:54:15 +02:00
#undef x
2021-03-01 07:54:51 +01:00
// Each array contains the columns that belong solely to one player, dependent on the number of players
// When determing how difficult the berries in a column should be, the highest
// difficulty of the players sharing that column is used.
// This table is used to skip that check, and instead automatically use the
// difficulty of the only player who can use the column.
static const u8 sUnsharedColumns[MAX_RFU_PLAYERS][MAX_RFU_PLAYERS] =
2019-08-01 19:01:50 +02:00
{
2019-08-02 00:49:31 +02:00
{5},
{4, 6},
{3, 5, 7},
{2, 4, 6, 8},
2021-03-01 07:54:51 +01:00
#ifndef BUGFIX
{1, 3, 5, 6, 9}, // BUG: Column 6 is shared, 7 is not. As a result, the player in column 7 will have their difficulty influenced by their neighbors
#else
{1, 3, 5, 7, 9},
#endif
2019-08-01 19:01:50 +02:00
};
// Duplicate and unused gfx. Feel free to remove.
2021-03-01 07:54:51 +01:00
static const u32 sDuplicateGfx[] = INCBIN_U32("graphics/dodrio_berry_picking/bg.gbapal",
"graphics/dodrio_berry_picking/tree_border.gbapal",
"graphics/dodrio_berry_picking/dodrio.gbapal",
"graphics/dodrio_berry_picking/shiny.gbapal",
"graphics/dodrio_berry_picking/status.gbapal",
"graphics/dodrio_berry_picking/berries.gbapal",
"graphics/dodrio_berry_picking/berries.4bpp.lz",
"graphics/dodrio_berry_picking/cloud.gbapal",
"graphics/dodrio_berry_picking/bg.4bpp.lz",
"graphics/dodrio_berry_picking/tree_border.4bpp.lz",
"graphics/dodrio_berry_picking/status.4bpp.lz",
"graphics/dodrio_berry_picking/cloud.4bpp.lz",
"graphics/dodrio_berry_picking/dodrio.4bpp.lz",
"graphics/dodrio_berry_picking/bg.bin.lz",
"graphics/dodrio_berry_picking/tree_border_right.bin.lz",
"graphics/dodrio_berry_picking/tree_border_left.bin.lz");
static const u8 sBerryFallDelays[][3] =
{
{ [BERRY_BLUE] = 40, [BERRY_GREEN] = 24, [BERRY_GOLD] = 13 },
{ [BERRY_BLUE] = 32, [BERRY_GREEN] = 19, [BERRY_GOLD] = 10 },
{ [BERRY_BLUE] = 22, [BERRY_GREEN] = 13, [BERRY_GOLD] = 7 },
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
// How far the outer tree borders should slide to reveal the game screen.
// Dependent on how many players are playing.
// Curiously the 2-player screen is narrower than the 1-player, though neither
// gets used as there's a 3 player minimum
2019-08-01 19:01:50 +02:00
ALIGNED(4)
2021-03-01 07:54:51 +01:00
static const u8 sTreeBorderXPos[MAX_RFU_PLAYERS] = {8, 5, 8, 11, 15};
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
// The number of berries eaten needed to progress to the next difficulty
2019-08-01 19:01:50 +02:00
ALIGNED(4)
2021-03-01 07:54:51 +01:00
static const u8 sDifficultyThresholds[NUM_DIFFICULTIES] = {5, 10, 20, 30, 50, 70, 100};
2019-08-01 19:01:50 +02:00
ALIGNED(4)
2021-03-01 07:54:51 +01:00
static const u8 sPrizeBerryIds[][10] =
{
{ // Possible prizes with 3 players
ITEM_TO_BERRY(ITEM_RAZZ_BERRY) - 1,
ITEM_TO_BERRY(ITEM_BLUK_BERRY) - 1,
ITEM_TO_BERRY(ITEM_NANAB_BERRY) - 1,
ITEM_TO_BERRY(ITEM_WEPEAR_BERRY) - 1,
ITEM_TO_BERRY(ITEM_PINAP_BERRY) - 1,
ITEM_TO_BERRY(ITEM_PINAP_BERRY) - 1,
ITEM_TO_BERRY(ITEM_WEPEAR_BERRY) - 1,
ITEM_TO_BERRY(ITEM_NANAB_BERRY) - 1,
ITEM_TO_BERRY(ITEM_BLUK_BERRY) - 1,
2021-03-01 07:54:51 +01:00
ITEM_TO_BERRY(ITEM_RAZZ_BERRY) - 1
},
{ // Possible prizes with 4 players
ITEM_TO_BERRY(ITEM_POMEG_BERRY) - 1,
ITEM_TO_BERRY(ITEM_KELPSY_BERRY) - 1,
ITEM_TO_BERRY(ITEM_QUALOT_BERRY) - 1,
ITEM_TO_BERRY(ITEM_HONDEW_BERRY) - 1,
ITEM_TO_BERRY(ITEM_GREPA_BERRY) - 1,
ITEM_TO_BERRY(ITEM_TAMATO_BERRY) - 1,
ITEM_TO_BERRY(ITEM_CORNN_BERRY) - 1,
ITEM_TO_BERRY(ITEM_MAGOST_BERRY) - 1,
ITEM_TO_BERRY(ITEM_RABUTA_BERRY) - 1,
ITEM_TO_BERRY(ITEM_NOMEL_BERRY) - 1
},
{ // Possible prizes with 5 players
ITEM_TO_BERRY(ITEM_SPELON_BERRY) - 1,
ITEM_TO_BERRY(ITEM_PAMTRE_BERRY) - 1,
ITEM_TO_BERRY(ITEM_WATMEL_BERRY) - 1,
ITEM_TO_BERRY(ITEM_DURIN_BERRY) - 1,
ITEM_TO_BERRY(ITEM_BELUE_BERRY) - 1,
ITEM_TO_BERRY(ITEM_BELUE_BERRY) - 1,
ITEM_TO_BERRY(ITEM_DURIN_BERRY) - 1,
ITEM_TO_BERRY(ITEM_WATMEL_BERRY) - 1,
ITEM_TO_BERRY(ITEM_PAMTRE_BERRY) - 1,
ITEM_TO_BERRY(ITEM_SPELON_BERRY) - 1
},
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static void (*const sLeaderFuncs[])(void) =
{
[FUNC_INTRO] = DoGameIntro,
[FUNC_INIT_COUNTDOWN] = InitCountdown,
[FUNC_COUNTDOWN] = DoCountdown,
[FUNC_WAIT_START] = WaitGameStart,
[FUNC_PLAY_GAME] = PlayGame_Leader,
[FUNC_INIT_RESULTS] = InitResults_Leader,
[FUNC_RESULTS] = DoResults,
[FUNC_ASK_PLAY_AGAIN] = AskPlayAgain,
[FUNC_END_LINK] = EndLink,
[FUNC_EXIT] = ExitGame,
[FUNC_RESET_GAME] = ResetGame,
[FUNC_WAIT_END_GAME] = WaitEndGame_Leader
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static void (*const sMemberFuncs[])(void) =
{
[FUNC_INTRO] = DoGameIntro,
[FUNC_INIT_COUNTDOWN] = InitCountdown,
[FUNC_COUNTDOWN] = DoCountdown,
[FUNC_WAIT_START] = WaitGameStart,
[FUNC_PLAY_GAME] = PlayGame_Member,
[FUNC_INIT_RESULTS] = InitResults_Member,
[FUNC_RESULTS] = DoResults,
[FUNC_ASK_PLAY_AGAIN] = AskPlayAgain,
[FUNC_END_LINK] = EndLink,
[FUNC_EXIT] = ExitGame,
[FUNC_RESET_GAME] = ResetGame,
[FUNC_WAIT_END_GAME] = WaitEndGame_Member
2019-07-31 00:27:30 +02:00
};
2021-03-01 07:54:51 +01:00
void StartDodrioBerryPicking(u16 partyId, void (*exitCallback)(void))
2019-03-24 21:42:42 +01:00
{
2021-03-01 07:54:51 +01:00
sExitingGame = FALSE;
2019-03-24 21:42:42 +01:00
2021-03-01 07:54:51 +01:00
if (gReceivedRemoteLinkPlayers != 0 && (sGame = AllocZeroed(sizeof(*sGame))))
2019-03-24 21:42:42 +01:00
{
2021-03-01 07:54:51 +01:00
ResetTasksAndSprites();
InitDodrioGame(sGame);
sGame->exitCallback = exitCallback;
sGame->multiplayerId = GetMultiplayerId();
sGame->player = sGame->players[sGame->multiplayerId];
InitMonInfo(&sGame->monInfo[sGame->multiplayerId], &gPlayerParty[partyId]);
CreateTask(Task_StartDodrioGame, 1);
SetMainCallback2(CB2_DodrioGame);
SetRandomPrize();
GetActiveBerryColumns(sGame->numPlayers, &sGame->berryColStart, &sGame->berryColEnd);
2019-03-24 21:42:42 +01:00
StopMapMusic();
2020-08-21 00:02:00 +02:00
PlayNewMapMusic(MUS_RG_BERRY_PICK);
2019-03-24 21:42:42 +01:00
}
else
{
2021-03-01 07:54:51 +01:00
// Exit - Alloc failed, or players not connected
SetMainCallback2(exitCallback);
2019-03-24 21:42:42 +01:00
return;
}
}
2021-03-01 07:54:51 +01:00
static void ResetTasksAndSprites(void)
2019-03-24 21:42:42 +01:00
{
ResetTasks();
ResetSpriteData();
FreeAllSpritePalettes();
}
2021-03-01 07:54:51 +01:00
static void InitDodrioGame(struct DodrioGame * game)
2019-03-24 21:42:42 +01:00
{
u8 i;
2021-03-01 07:54:51 +01:00
game->startState = 0;
game->state = 0;
game->timer = 0;
game->funcId = FUNC_INTRO;
game->prevFuncId = FUNC_INTRO;
game->startGame = FALSE;
game->berriesFalling = FALSE;
game->countdownEndDelay = 0;
game->numGraySquares = 0;
game->unused2 = 0;
game->allReadyToEnd = FALSE;
for (i = 0; i < ARRAY_COUNT(game->pickStateQueue); i++)
game->pickStateQueue[i] = PICK_NONE;
for (i = 0; i < MAX_RFU_PLAYERS; i++)
{
game->inputState[i] = INPUTSTATE_NONE;
game->inputDelay[i] = 0;
game->berryResults[i][BERRY_BLUE] = 0;
game->berryResults[i][BERRY_GREEN] = 0;
game->berryResults[i][BERRY_GOLD] = 0;
game->berryResults[i][BERRY_MISSED] = 0;
game->berryResults[i][BERRY_IN_ROW] = 0;
game->playAgainStates[i] = PLAY_AGAIN_NONE;
game->readyToEnd[i] = FALSE;
2019-03-24 21:42:42 +01:00
}
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_BERRY_COLUMNS; i++)
2019-03-24 21:42:42 +01:00
{
2021-03-01 07:54:51 +01:00
game->fallTimer[i] = 0;
game->newBerryTimer[i] = 0;
game->berryState[i] = BERRYSTATE_NONE;
game->playersAttemptingPick[i][0] = PLAYER_NONE;
game->playersAttemptingPick[i][1] = PLAYER_NONE;
2019-03-24 21:42:42 +01:00
}
2021-03-01 07:54:51 +01:00
game->isLeader = GetMultiplayerId() == 0 ? TRUE : FALSE;
game->numPlayers = GetLinkPlayerCount();
game->posToPlayerId[0] = GetMultiplayerId();
for (i = 1; i < game->numPlayers; i++)
2019-03-24 21:42:42 +01:00
{
2021-03-01 07:54:51 +01:00
game->posToPlayerId[i] = game->posToPlayerId[i - 1] + 1;
if (game->posToPlayerId[i] > game->numPlayers - 1)
game->posToPlayerId[i] %= game->numPlayers;
2019-03-24 21:42:42 +01:00
}
}
2019-03-24 22:32:59 +01:00
2021-03-01 07:54:51 +01:00
static void Task_StartDodrioGame(u8 taskId)
2019-03-24 22:32:59 +01:00
{
2021-03-01 07:54:51 +01:00
u8 i, numPlayers;
2019-03-24 22:32:59 +01:00
2021-03-01 07:54:51 +01:00
switch (sGame->startState)
2019-03-24 22:32:59 +01:00
{
2019-03-25 23:08:25 +01:00
case 0:
SetVBlankCallback(NULL);
2021-03-01 07:54:51 +01:00
CreateTask_(Task_CommunicateMonInfo, 4);
sGame->startState++;
2019-03-25 23:08:25 +01:00
break;
case 1:
2021-03-01 07:54:51 +01:00
if (!FuncIsActiveTask(Task_CommunicateMonInfo))
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
InitGameGfx(&sGame->gfx);
sGame->startState++;
2019-03-25 23:08:25 +01:00
}
break;
case 2:
2021-03-01 07:54:51 +01:00
if (!IsGfxFuncActive())
2019-03-25 23:08:25 +01:00
{
2020-08-13 09:09:47 +02:00
Rfu_SetLinkStandbyCallback();
2021-03-01 07:54:51 +01:00
sGame->startState++;
2019-03-25 23:08:25 +01:00
}
break;
case 3:
if (IsLinkTaskFinished())
{
if (gReceivedRemoteLinkPlayers != 0)
2019-03-24 22:32:59 +01:00
{
LoadWirelessStatusIndicatorSpriteGfx();
2019-03-25 23:08:25 +01:00
CreateWirelessStatusIndicatorSprite(0, 0);
2019-03-24 22:32:59 +01:00
}
2021-03-01 07:54:51 +01:00
sGame->startState++;
2019-03-25 23:08:25 +01:00
}
break;
case 4:
2021-03-01 07:54:51 +01:00
numPlayers = sGame->numPlayers;
LoadDodrioGfx();
for (i = 0; i < numPlayers; i++)
CreateDodrioSprite(&sGame->monInfo[sGame->posToPlayerId[i]], i, sGame->posToPlayerId[i], sGame->numPlayers);
SetAllDodrioInvisibility(FALSE, sGame->numPlayers);
sGame->startState++;
2019-03-25 23:08:25 +01:00
break;
case 5:
2021-03-01 07:54:51 +01:00
LoadBerryGfx();
CreateBerrySprites();
CreateCloudSprites();
CreateStatusBarSprites();
sGame->startState++;
2019-03-25 23:08:25 +01:00
break;
case 6:
2021-02-24 17:01:02 +01:00
BlendPalettes(PALETTES_ALL, 0x10, 0x00);
BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, 0);
2021-03-01 07:54:51 +01:00
SetVBlankCallback(VBlankCB_DodrioGame);
sGame->startState++;
2019-03-25 23:08:25 +01:00
break;
case 7:
UpdatePaletteFade();
if (!gPaletteFade.active)
2021-03-01 07:54:51 +01:00
sGame->startState++;
2019-03-25 23:08:25 +01:00
break;
default:
DestroyTask(taskId);
2021-03-01 07:54:51 +01:00
CreateDodrioGameTask(Task_NewGameIntro);
2019-03-25 23:08:25 +01:00
break;
2019-03-24 22:32:59 +01:00
}
}
2019-03-25 02:17:25 +01:00
2021-03-01 07:54:51 +01:00
static void Task_DodrioGame_Leader(u8 taskId)
2019-03-25 02:17:25 +01:00
{
2021-03-01 07:54:51 +01:00
RecvLinkData_Leader();
sLeaderFuncs[sGame->funcId]();
if (!sExitingGame)
UpdateGame_Leader();
SendLinkData_Leader();
2019-03-25 02:17:25 +01:00
}
2021-03-01 07:54:51 +01:00
static void Task_DodrioGame_Member(u8 taskId)
2019-03-25 02:17:25 +01:00
{
2021-03-01 07:54:51 +01:00
RecvLinkData_Member();
sMemberFuncs[sGame->funcId]();
if (!sExitingGame)
UpdateGame_Member();
SendLinkData_Member();
2019-03-25 02:17:25 +01:00
}
2021-03-01 07:54:51 +01:00
static void DoGameIntro(void)
2019-03-25 02:17:25 +01:00
{
2021-03-01 07:54:51 +01:00
switch (sGame->state)
2019-03-25 02:17:25 +01:00
{
2019-03-25 23:08:25 +01:00
case 0:
2021-03-01 07:54:51 +01:00
StartDodrioIntroAnim(1);
SetGfxFuncById(GFXFUNC_SHOW_NAMES);
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 1:
2021-03-01 07:54:51 +01:00
if (!IsGfxFuncActive())
SetGameFunc(FUNC_INIT_COUNTDOWN);
2019-03-25 23:08:25 +01:00
break;
2019-03-25 02:17:25 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void InitCountdown(void)
2019-03-25 02:17:25 +01:00
{
2021-03-01 07:54:51 +01:00
switch (sGame->state)
2019-03-25 02:17:25 +01:00
{
2021-03-01 07:54:51 +01:00
case 0:
InitFirstWaveOfBerries();
sGame->state++;
break;
default:
sGame->startCountdown = TRUE;
SetGameFunc(FUNC_COUNTDOWN);
break;
2019-03-25 02:17:25 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void DoCountdown(void)
2019-03-25 02:17:25 +01:00
{
2021-03-01 07:54:51 +01:00
switch (sGame->state)
2019-03-25 02:17:25 +01:00
{
2019-03-25 23:08:25 +01:00
case 0:
2021-03-11 20:16:40 +01:00
StartMinigameCountdown(GFXTAG_COUNTDOWN, PALTAG_COUNTDOWN, 120, 80, 0);
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 1:
2020-08-13 09:09:47 +02:00
Rfu_SetLinkStandbyCallback();
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 2:
if (IsLinkTaskFinished())
{
2021-03-01 07:54:51 +01:00
sGame->state++;
sGame->countdownEndDelay = 0;
2019-03-25 23:08:25 +01:00
}
break;
case 3:
2020-04-08 23:23:32 +02:00
if (!IsMinigameCountdownRunning())
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 4:
2021-03-01 07:54:51 +01:00
if (++sGame->countdownEndDelay > 5)
2019-03-25 23:08:25 +01:00
{
2020-08-13 09:09:47 +02:00
Rfu_SetLinkStandbyCallback();
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
case 5:
if (IsLinkTaskFinished())
2021-03-01 07:54:51 +01:00
SetGameFunc(FUNC_WAIT_START);
2019-03-25 23:08:25 +01:00
break;
2019-03-25 02:17:25 +01:00
}
}
2019-03-25 03:56:16 +01:00
2021-03-01 07:54:51 +01:00
static void WaitGameStart(void)
2019-03-25 03:56:16 +01:00
{
2021-03-01 07:54:51 +01:00
switch (sGame->state)
2019-03-25 03:56:16 +01:00
{
2021-03-01 07:54:51 +01:00
case 0:
if (sGame->startGame)
SetGameFunc(FUNC_PLAY_GAME);
break;
2019-03-25 03:56:16 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void PlayGame_Leader(void)
2019-03-25 03:56:16 +01:00
{
2021-03-01 07:54:51 +01:00
switch (sGame->state)
2019-03-25 03:56:16 +01:00
{
2021-03-01 07:54:51 +01:00
case 0:
if (sGame->numGraySquares < NUM_STATUS_SQUARES)
2019-03-25 03:56:16 +01:00
{
2021-03-01 07:54:51 +01:00
if (sGame->inputState[0] == INPUTSTATE_NONE)
2019-03-25 03:56:16 +01:00
{
if (JOY_NEW(DPAD_UP))
{
2021-03-01 07:54:51 +01:00
if (sGame->players[0].comm.pickState == PICK_NONE)
2019-03-25 03:56:16 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->players[0].comm.ateBerry = FALSE;
sGame->players[0].comm.pickState = UpdatePickStateQueue(PICK_MIDDLE);
2019-03-25 03:56:16 +01:00
}
}
else if (JOY_NEW(DPAD_RIGHT))
{
2021-03-01 07:54:51 +01:00
if (sGame->players[0].comm.pickState == PICK_NONE)
2019-03-25 03:56:16 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->players[0].comm.ateBerry = FALSE;
sGame->players[0].comm.pickState = UpdatePickStateQueue(PICK_RIGHT);
2019-03-25 03:56:16 +01:00
}
}
else if (JOY_NEW(DPAD_LEFT))
{
2021-03-01 07:54:51 +01:00
if (sGame->players[0].comm.pickState == PICK_NONE)
2019-03-25 03:56:16 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->players[0].comm.ateBerry = FALSE;
sGame->players[0].comm.pickState = UpdatePickStateQueue(PICK_LEFT);
2019-03-25 03:56:16 +01:00
}
}
else
{
2021-03-01 07:54:51 +01:00
sGame->players[0].comm.pickState = UpdatePickStateQueue(PICK_NONE);
2019-03-25 03:56:16 +01:00
}
}
}
else
{
2021-03-01 07:54:51 +01:00
SetGameFunc(FUNC_WAIT_END_GAME);
2019-03-25 03:56:16 +01:00
}
2021-03-01 07:54:51 +01:00
UpdateFallingBerries();
HandleSound_Leader();
break;
2019-03-25 03:56:16 +01:00
}
}
2019-03-25 13:31:29 +01:00
2021-03-01 07:54:51 +01:00
static void PlayGame_Member(void)
2019-03-25 13:31:29 +01:00
{
2021-03-01 07:54:51 +01:00
if (sGame->numGraySquares < NUM_STATUS_SQUARES)
2019-03-25 13:31:29 +01:00
{
if (JOY_NEW(DPAD_UP))
{
2021-03-01 07:54:51 +01:00
if (sGame->players[sGame->multiplayerId].comm.pickState == PICK_NONE)
2019-03-25 13:31:29 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->player.comm.pickState = PICK_MIDDLE;
2019-03-25 13:31:29 +01:00
}
}
else if (JOY_NEW(DPAD_RIGHT))
{
2021-03-01 07:54:51 +01:00
if (sGame->players[sGame->multiplayerId].comm.pickState == PICK_NONE)
2019-03-25 13:31:29 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->player.comm.pickState = PICK_RIGHT;
2019-03-25 13:31:29 +01:00
}
}
else if (JOY_NEW(DPAD_LEFT))
{
2021-03-01 07:54:51 +01:00
if (sGame->players[sGame->multiplayerId].comm.pickState == PICK_NONE)
2019-03-25 13:31:29 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->player.comm.pickState = PICK_LEFT;
2019-03-25 13:31:29 +01:00
}
}
else
{
2021-03-01 07:54:51 +01:00
sGame->player.comm.pickState = PICK_NONE;
2019-03-25 13:31:29 +01:00
}
}
else
{
2021-03-01 07:54:51 +01:00
SetGameFunc(FUNC_WAIT_END_GAME);
2019-03-25 13:31:29 +01:00
}
2021-03-01 07:54:51 +01:00
HandleSound_Member();
2019-03-25 13:31:29 +01:00
}
2019-03-25 16:22:48 +01:00
2021-03-01 07:54:51 +01:00
static void WaitEndGame_Leader(void)
2019-03-25 16:22:48 +01:00
{
u8 i;
2021-03-01 07:54:51 +01:00
UpdateFallingBerries();
HandleSound_Leader();
if (ReadyToEndGame_Leader() == TRUE)
2019-03-25 16:22:48 +01:00
{
2021-03-01 07:54:51 +01:00
SetMaxBerriesPickedInRow();
SetGameFunc(FUNC_INIT_RESULTS);
2019-03-25 16:22:48 +01:00
}
else
{
2021-03-01 07:54:51 +01:00
sGame->allReadyToEnd = TRUE;
for (i = 1; i < sGame->numPlayers; i++)
2019-03-25 16:22:48 +01:00
{
2021-03-01 07:54:51 +01:00
if (sGame->readyToEnd[i] != TRUE)
2019-03-25 16:22:48 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->allReadyToEnd = FALSE;
2019-03-25 16:22:48 +01:00
break;
}
}
}
}
2021-03-01 07:54:51 +01:00
static void WaitEndGame_Member(void)
2019-03-25 16:22:48 +01:00
{
2021-03-01 07:54:51 +01:00
HandleSound_Member();
if (ReadyToEndGame_Member() == TRUE)
SetGameFunc(FUNC_INIT_RESULTS);
2019-03-25 16:22:48 +01:00
}
2021-03-01 07:54:51 +01:00
static bool32 AllLinkBlocksReceived(void)
2019-03-25 16:22:48 +01:00
{
u8 recvStatus = GetBlockReceivedStatus();
u8 playerFlags = GetLinkPlayerCountAsBitFlags();
if (recvStatus == playerFlags)
2019-03-25 16:22:48 +01:00
{
ResetBlockReceivedFlags();
return TRUE;
}
else
{
return FALSE;
}
}
2021-03-01 07:54:51 +01:00
static void InitResults_Leader(void)
2019-03-25 16:22:48 +01:00
{
2021-03-01 07:54:51 +01:00
switch (sGame->state)
2019-03-25 16:22:48 +01:00
{
2019-03-25 23:08:25 +01:00
case 0:
2021-03-01 07:54:51 +01:00
if (SendBlock(0, sGame->berryResults, sizeof(sGame->berryResults)))
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->playersReceived = 0;
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
case 1:
if (IsLinkTaskFinished())
{
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
case 2:
2021-03-01 07:54:51 +01:00
if (AllLinkBlocksReceived())
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->playersReceived = sGame->numPlayers;
2019-03-25 23:08:25 +01:00
}
2021-03-01 07:54:51 +01:00
if (sGame->playersReceived >= sGame->numPlayers)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->timer++;
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
default:
if (WaitFanfare(TRUE))
{
2021-03-01 07:54:51 +01:00
SetGameFunc(FUNC_RESULTS);
2020-08-21 00:02:00 +02:00
FadeOutAndPlayNewMapMusic(MUS_RG_VICTORY_WILD, 4);
2019-03-25 23:08:25 +01:00
}
break;
2019-03-25 16:22:48 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void InitResults_Member(void)
2019-03-25 16:22:48 +01:00
{
u8 i;
2021-03-01 07:54:51 +01:00
switch (sGame->state) {
2019-03-25 23:08:25 +01:00
case 0:
2021-03-01 07:54:51 +01:00
if (SendBlock(0, sGame->berryResults[sGame->timer], sizeof(sGame->berryResults))) {
sGame->playersReceived = 0;
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
case 1:
if (IsLinkTaskFinished()) {
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
case 2:
2021-03-01 07:54:51 +01:00
if (AllLinkBlocksReceived()) {
for (i = 0; i < sGame->numPlayers; i++) {
memcpy(sGame->berryResults, gBlockRecvBuffer, sizeof(sGame->berryResults));
sGame->playersReceived = sGame->numPlayers;
2019-03-25 16:22:48 +01:00
}
2019-03-25 23:08:25 +01:00
}
2021-03-01 07:54:51 +01:00
if (sGame->playersReceived >= sGame->numPlayers) {
sGame->timer++;
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
default:
if (WaitFanfare(TRUE)) {
2021-03-01 07:54:51 +01:00
sGame->maxBerriesPickedInRow = sGame->berryResults[sGame->multiplayerId][BERRY_IN_ROW];
SetGameFunc(FUNC_RESULTS);
2020-08-21 00:02:00 +02:00
FadeOutAndPlayNewMapMusic(MUS_RG_VICTORY_WILD, 4);
2019-03-25 23:08:25 +01:00
}
break;
2019-03-25 16:22:48 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void DoResults(void)
2019-03-25 16:22:48 +01:00
{
2021-03-01 07:54:51 +01:00
u8 playAgainState = PLAY_AGAIN_YES;
2019-03-25 16:22:48 +01:00
u8 i;
2021-03-01 07:54:51 +01:00
switch (sGame->state)
2019-03-25 16:22:48 +01:00
{
2019-03-25 23:08:25 +01:00
case 0:
2021-03-01 07:54:51 +01:00
TryUpdateRecords();
SetStatusBarInvisibility(TRUE);
ResetCloudPos();
SetCloudInvisibility(TRUE);
SetGfxFuncById(GFXFUNC_SHOW_RESULTS);
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 1:
2021-03-01 07:54:51 +01:00
if (!IsGfxFuncActive())
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
SetGfxFuncById(GFXFUNC_MSG_COMM_STANDBY);
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
case 2:
2021-03-01 07:54:51 +01:00
playAgainState = GetPlayAgainState();
if (SendBlock(0, &playAgainState, sizeof(playAgainState)))
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 3:
if (IsLinkTaskFinished())
{
2021-03-01 07:54:51 +01:00
sGame->state++;
sGame->playersReceived = 0;
2019-03-25 23:08:25 +01:00
}
break;
case 4:
2021-03-01 07:54:51 +01:00
if (AllLinkBlocksReceived())
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
for (i = 0; i < sGame->numPlayers; i++)
2019-03-25 16:22:48 +01:00
{
2021-03-01 07:54:51 +01:00
*(&sGame->playAgainStates[i]) = *(u8 *)gBlockRecvBuffer[i];
sGame->playersReceived = sGame->numPlayers;
2019-03-25 16:22:48 +01:00
}
2019-03-25 23:08:25 +01:00
}
2021-03-01 07:54:51 +01:00
if (sGame->playersReceived >= sGame->numPlayers)
{
if (++sGame->timer >= 120)
2019-03-25 16:22:48 +01:00
{
2021-03-01 07:54:51 +01:00
SetGfxFuncById(GFXFUNC_ERASE_MSG);
sGame->state++;
2019-03-25 16:22:48 +01:00
}
2019-03-25 23:08:25 +01:00
}
break;
default:
2021-03-01 07:54:51 +01:00
if (!IsGfxFuncActive())
SetGameFunc(FUNC_ASK_PLAY_AGAIN);
2019-03-25 23:08:25 +01:00
break;
2019-03-25 16:22:48 +01:00
}
}
2019-03-25 17:08:56 +01:00
2021-03-01 07:54:51 +01:00
static void AskPlayAgain(void)
2019-03-25 17:08:56 +01:00
{
2021-03-01 07:54:51 +01:00
u8 playAgainState;
2019-03-25 17:08:56 +01:00
u8 i;
2021-03-01 07:54:51 +01:00
switch (sGame->state)
2019-03-25 17:08:56 +01:00
{
2019-03-25 23:08:25 +01:00
case 0:
2021-03-01 07:54:51 +01:00
if (GetHighestScore() >= PRIZE_SCORE)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
SetGfxFuncById(GFXFUNC_MSG_SAVING);
2019-03-25 23:08:25 +01:00
}
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 1:
2021-03-01 07:54:51 +01:00
if (!IsGfxFuncActive())
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
SetGfxFuncById(GFXFUNC_MSG_PLAY_AGAIN);
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
case 2:
2021-03-01 07:54:51 +01:00
ResetBerryAndStatusBarSprites();
ResetForPlayAgainPrompt();
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 3:
2021-03-01 07:54:51 +01:00
if ((playAgainState = GetPlayAgainState()) != PLAY_AGAIN_NONE)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
case 4:
2021-03-01 07:54:51 +01:00
if (!IsGfxFuncActive())
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
SetGfxFuncById(GFXFUNC_MSG_COMM_STANDBY);
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
case 5:
2021-03-01 07:54:51 +01:00
playAgainState = GetPlayAgainState();
if (SendBlock(0, &playAgainState, sizeof(playAgainState)))
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->playersReceived = 0;
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
case 6:
if (IsLinkTaskFinished())
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 7:
2021-03-01 07:54:51 +01:00
if (AllLinkBlocksReceived())
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
for (i = 0; i < sGame->numPlayers; i++)
2019-03-25 17:08:56 +01:00
{
2021-03-01 07:54:51 +01:00
*(&sGame->playAgainStates[i]) = *(u8 *)gBlockRecvBuffer[i];
sGame->playersReceived = sGame->numPlayers;
2019-03-25 17:08:56 +01:00
}
2019-03-25 23:08:25 +01:00
}
2021-03-01 07:54:51 +01:00
if (sGame->playersReceived >= sGame->numPlayers)
{
if (++sGame->timer >= 120)
2019-03-25 17:08:56 +01:00
{
2021-03-01 07:54:51 +01:00
ResetPickState();
SetGfxFuncById(GFXFUNC_ERASE_MSG);
sGame->state++;
2019-03-25 17:08:56 +01:00
}
2019-03-25 23:08:25 +01:00
}
else
{
2021-03-01 07:54:51 +01:00
HandleWaitPlayAgainInput();
2019-03-25 23:08:25 +01:00
}
break;
default:
2021-03-01 07:54:51 +01:00
if (!IsGfxFuncActive())
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
for (i = 0; i < sGame->numPlayers; i++)
2019-03-25 17:08:56 +01:00
{
2021-03-01 07:54:51 +01:00
if (sGame->playAgainStates[i] == PLAY_AGAIN_NO)
2019-03-25 17:08:56 +01:00
{
2021-03-01 07:54:51 +01:00
SetGameFunc(FUNC_END_LINK);
2019-03-25 23:08:25 +01:00
return;
2019-03-25 17:08:56 +01:00
}
}
2021-03-01 07:54:51 +01:00
SetGameFunc(FUNC_RESET_GAME);
2019-03-25 23:08:25 +01:00
}
break;
2019-03-25 17:08:56 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void EndLink(void)
2019-03-25 17:08:56 +01:00
{
2021-03-01 07:54:51 +01:00
switch (sGame->state)
2019-03-25 17:08:56 +01:00
{
2019-03-25 23:08:25 +01:00
case 0:
2020-08-13 09:09:47 +02:00
SetCloseLinkCallback();
2021-03-01 07:54:51 +01:00
SetGfxFuncById(GFXFUNC_MSG_PLAYER_DROPPED);
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 1:
2021-03-01 07:54:51 +01:00
if (!IsGfxFuncActive())
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 2:
2021-03-01 07:54:51 +01:00
if (GetPlayAgainState() == PLAY_AGAIN_DROPPED)
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
default:
if (gReceivedRemoteLinkPlayers == 0)
{
2021-03-01 07:54:51 +01:00
SetGameFunc(FUNC_EXIT);
2019-03-25 23:08:25 +01:00
}
break;
2019-03-25 17:08:56 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void ExitGame(void)
2019-03-25 17:08:56 +01:00
{
2021-03-01 07:54:51 +01:00
switch (sGame->state)
2019-03-25 17:08:56 +01:00
{
2019-03-25 23:08:25 +01:00
case 0:
2021-02-24 17:01:02 +01:00
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, 0);
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 1:
UpdatePaletteFade();
if (!gPaletteFade.active)
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 2:
2021-03-01 07:54:51 +01:00
FreeBerrySprites();
FreeStatusBar();
FreeDodrioSprites(sGame->numPlayers);
FreeCloudSprites();
sExitingGame = TRUE;
SetGfxFuncById(GFXFUNC_STOP);
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
default:
2021-03-01 07:54:51 +01:00
if (!IsGfxFuncActive())
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
SetMainCallback2(sGame->exitCallback);
DestroyTask(sGame->taskId);
Free(sGame);
2019-03-25 23:08:25 +01:00
FreeAllWindowBuffers();
}
break;
2019-03-25 17:08:56 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void ResetGame(void)
2019-03-25 17:08:56 +01:00
{
2021-03-01 07:54:51 +01:00
switch (sGame->state)
2019-03-25 17:08:56 +01:00
{
2019-03-25 23:08:25 +01:00
case 0:
2021-03-01 07:54:51 +01:00
SetGfxFuncById(GFXFUNC_IDLE);
2021-02-24 17:01:02 +01:00
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, 0);
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 1:
UpdatePaletteFade();
if (!gPaletteFade.active)
{
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
case 2:
2021-11-04 04:02:06 +01:00
ChangeBgX(0, 0, BG_COORD_SET);
ChangeBgY(0, 0, BG_COORD_SET);
ChangeBgX(1, 0, BG_COORD_SET);
ChangeBgY(1, 0, BG_COORD_SET);
ChangeBgX(2, 0, BG_COORD_SET);
ChangeBgY(2, 0, BG_COORD_SET);
ChangeBgX(3, 0, BG_COORD_SET);
ChangeBgY(3, 0, BG_COORD_SET);
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 3:
StopMapMusic();
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 4:
2020-08-21 00:02:00 +02:00
PlayNewMapMusic(MUS_RG_BERRY_PICK);
2021-03-01 07:54:51 +01:00
StartCloudMovement();
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 5:
2021-02-24 17:01:02 +01:00
BlendPalettes(PALETTES_ALL, 16, 0);
BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, 0);
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 6:
UpdatePaletteFade();
if (!gPaletteFade.active)
2021-03-01 07:54:51 +01:00
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
default:
2021-03-01 07:54:51 +01:00
DestroyTask(sGame->taskId);
CreateDodrioGameTask(Task_NewGameIntro);
ResetGfxState();
InitDodrioGame(sGame);
2019-03-25 23:08:25 +01:00
if (gReceivedRemoteLinkPlayers == 0)
2021-03-01 07:54:51 +01:00
sGame->numPlayers = 1;
SetRandomPrize();
SetCloudInvisibility(FALSE);
2019-03-25 23:08:25 +01:00
break;
2019-03-25 17:08:56 +01:00
}
}
2019-03-25 20:26:23 +01:00
2021-03-01 07:54:51 +01:00
static void Task_NewGameIntro(u8 taskId)
2019-03-25 20:26:23 +01:00
{
2021-03-01 07:54:51 +01:00
switch (sGame->state)
2019-03-25 20:26:23 +01:00
{
2019-03-25 23:08:25 +01:00
case 0:
2021-03-01 07:54:51 +01:00
if (SlideTreeBordersOut() == TRUE)
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 1:
2021-03-01 07:54:51 +01:00
InitStatusBarPos();
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
case 2:
2021-03-01 07:54:51 +01:00
if (DoStatusBarIntro() == TRUE)
sGame->state++;
2019-03-25 23:08:25 +01:00
break;
default:
2021-03-01 07:54:51 +01:00
if (sGame->isLeader)
CreateDodrioGameTask(Task_DodrioGame_Leader);
2019-03-25 23:08:25 +01:00
else
2021-03-01 07:54:51 +01:00
CreateDodrioGameTask(Task_DodrioGame_Member);
2019-03-25 23:08:25 +01:00
DestroyTask(taskId);
break;
2019-03-25 20:26:23 +01:00
}
}
2021-03-01 07:54:51 +01:00
#define tState data[0]
static void Task_CommunicateMonInfo(u8 taskId)
2019-03-25 20:26:23 +01:00
{
s16 * data = gTasks[taskId].data;
u8 i;
2021-03-01 07:54:51 +01:00
switch (tState)
2019-03-25 20:26:23 +01:00
{
2019-03-25 23:08:25 +01:00
case 0:
2021-03-01 07:54:51 +01:00
if (SendBlock(0, &sGame->monInfo[sGame->multiplayerId].isShiny, sizeof(sGame->monInfo[sGame->multiplayerId].isShiny)))
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->playersReceived = 0;
tState++;
2019-03-25 23:08:25 +01:00
}
break;
case 1:
if (IsLinkTaskFinished())
2021-03-01 07:54:51 +01:00
tState++;
2019-03-25 23:08:25 +01:00
break;
case 2:
2021-03-01 07:54:51 +01:00
if (AllLinkBlocksReceived())
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
for (i = 0; i < sGame->numPlayers; i++)
2019-03-25 20:26:23 +01:00
{
2021-03-01 07:54:51 +01:00
*(u8 *)&sGame->monInfo[i] = *(u8 *)gBlockRecvBuffer[i];
sGame->playersReceived = sGame->numPlayers;
2019-03-25 20:26:23 +01:00
}
2019-03-25 23:08:25 +01:00
}
2021-03-01 07:54:51 +01:00
if (sGame->playersReceived >= sGame->numPlayers)
2019-03-25 23:08:25 +01:00
{
DestroyTask(taskId);
2021-03-01 07:54:51 +01:00
SetGfxFuncById(GFXFUNC_ERASE_MSG);
sGame->state++;
2019-03-25 23:08:25 +01:00
}
break;
2019-03-25 20:26:23 +01:00
}
}
2021-03-01 07:54:51 +01:00
#undef tState
static void RecvLinkData_Gameplay(void)
2019-03-25 20:26:23 +01:00
{
u8 i;
2021-03-01 07:54:51 +01:00
u8 numPlayers = sGame->numPlayers;
sGame->players[0].receivedGameStatePacket = RecvPacket_GameState(0,
&sGame->players[0],
&sGame->players[0].comm,
&sGame->players[1].comm,
&sGame->players[2].comm,
&sGame->players[3].comm,
&sGame->players[4].comm,
&sGame->numGraySquares,
&sGame->berriesFalling,
2021-03-01 07:54:51 +01:00
&sGame->allReadyToEnd);
sGame->clearRecvCmds = TRUE;
for (i = 1; i < numPlayers; i++)
{
if (sGame->inputState[i] == INPUTSTATE_NONE && !RecvPacket_PickState(i, &sGame->players[i].comm.pickState))
2019-03-25 20:26:23 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->players[i].comm.pickState = PICK_NONE;
sGame->clearRecvCmds = FALSE;
2019-03-25 20:26:23 +01:00
}
}
2021-03-01 07:54:51 +01:00
if (++sGame->clearRecvCmdTimer >= 60)
2019-03-25 20:26:23 +01:00
{
2021-03-01 07:54:51 +01:00
if (sGame->clearRecvCmds)
2019-03-25 20:26:23 +01:00
{
2021-03-01 07:54:51 +01:00
ClearRecvCommands();
sGame->clearRecvCmdTimer = 0;
2019-03-25 20:26:23 +01:00
}
2021-03-01 07:54:51 +01:00
else if (sGame->clearRecvCmdTimer > 70)
2019-03-25 20:26:23 +01:00
{
2021-03-01 07:54:51 +01:00
ClearRecvCommands();
sGame->clearRecvCmdTimer = 0;
2019-03-25 20:26:23 +01:00
}
}
2021-03-01 07:54:51 +01:00
for (i = 0; i < numPlayers; i++)
2019-03-25 20:26:23 +01:00
{
2021-03-01 07:54:51 +01:00
if (sGame->players[i].comm.pickState != PICK_NONE && sGame->inputState[i] == INPUTSTATE_NONE)
2019-03-25 20:26:23 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->inputState[i] = INPUTSTATE_TRY_PICK;
2019-03-25 20:26:23 +01:00
}
2021-03-01 07:54:51 +01:00
switch (sGame->inputState[i])
2019-03-25 20:26:23 +01:00
{
2021-03-01 07:54:51 +01:00
case INPUTSTATE_NONE:
2019-03-25 23:08:25 +01:00
default:
break;
2021-03-01 07:54:51 +01:00
case INPUTSTATE_TRY_PICK:
case INPUTSTATE_PICKED:
case INPUTSTATE_ATE_BERRY:
if (++sGame->inputDelay[i] >= 6)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->inputDelay[i] = 0;
sGame->inputState[i] = INPUTSTATE_NONE;
sGame->players[i].comm.pickState = PICK_NONE;
sGame->players[i].comm.ateBerry = FALSE;
sGame->players[i].comm.missedBerry = FALSE;
2019-03-25 23:08:25 +01:00
}
break;
2021-03-01 07:54:51 +01:00
case INPUTSTATE_BAD_MISS:
// Tried to pick with no berry in range, long delay until next input
if (++sGame->inputDelay[i] >= 40)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->inputDelay[i] = 0;
sGame->inputState[i] = INPUTSTATE_NONE;
sGame->players[i].comm.pickState = PICK_NONE;
sGame->players[i].comm.ateBerry = FALSE;
sGame->players[i].comm.missedBerry = FALSE;
2019-03-25 23:08:25 +01:00
}
break;
2019-03-25 20:26:23 +01:00
}
}
}
2019-03-25 22:27:27 +01:00
2021-03-01 07:54:51 +01:00
static void RecvLinkData_ReadyToEnd(void)
2019-03-25 22:27:27 +01:00
{
u8 i;
2021-03-01 07:54:51 +01:00
u8 numPlayers = sGame->numPlayers;
sGame->players[0].receivedGameStatePacket = RecvPacket_GameState(0,
&sGame->players[0],
&sGame->players[0].comm,
&sGame->players[1].comm,
&sGame->players[2].comm,
&sGame->players[3].comm,
&sGame->players[4].comm,
&sGame->numGraySquares,
&sGame->berriesFalling,
2021-03-01 07:54:51 +01:00
&sGame->allReadyToEnd);
sGame->clearRecvCmds = TRUE;
for (i = 1; i < numPlayers; i++)
{
if (RecvPacket_ReadyToEnd(i))
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->readyToEnd[i] = TRUE;
sGame->clearRecvCmds = FALSE;
2019-03-25 22:27:27 +01:00
}
}
2021-03-01 07:54:51 +01:00
if (++sGame->clearRecvCmdTimer >= 60)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
if (sGame->clearRecvCmds)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
ClearRecvCommands();
sGame->clearRecvCmdTimer = 0;
2019-03-25 22:27:27 +01:00
}
2021-03-01 07:54:51 +01:00
else if (sGame->clearRecvCmdTimer > 70)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
ClearRecvCommands();
sGame->clearRecvCmdTimer = 0;
2019-03-25 22:27:27 +01:00
}
}
}
2021-03-01 07:54:51 +01:00
static void RecvLinkData_Leader(void)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
switch (sGame->funcId)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
case FUNC_WAIT_START:
if (AllPlayersReadyToStart() == TRUE)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
ResetReadyToStart();
sGame->startGame = TRUE;
2019-03-25 23:08:25 +01:00
}
break;
2021-03-01 07:54:51 +01:00
case FUNC_PLAY_GAME:
RecvLinkData_Gameplay();
2019-03-25 23:08:25 +01:00
break;
2021-03-01 07:54:51 +01:00
case FUNC_WAIT_END_GAME:
RecvLinkData_ReadyToEnd();
2019-03-25 23:08:25 +01:00
break;
2019-03-25 22:27:27 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void SendLinkData_Leader(void)
{
switch (sGame->funcId)
{
case FUNC_PLAY_GAME:
SendPacket_GameState(&sGame->player,
&sGame->players[0].comm,
&sGame->players[1].comm,
&sGame->players[2].comm,
&sGame->players[3].comm,
&sGame->players[4].comm,
sGame->numGraySquares,
sGame->berriesFalling,
2021-03-01 07:54:51 +01:00
sGame->allReadyToEnd);
break;
case FUNC_WAIT_END_GAME:
SendPacket_GameState(&sGame->player,
&sGame->players[0].comm,
&sGame->players[1].comm,
&sGame->players[2].comm,
&sGame->players[3].comm,
&sGame->players[4].comm,
sGame->numGraySquares,
sGame->berriesFalling,
2021-03-01 07:54:51 +01:00
sGame->allReadyToEnd);
2019-03-25 23:08:25 +01:00
break;
2019-03-25 22:27:27 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void RecvLinkData_Member(void)
{
switch (sGame->funcId)
{
case FUNC_PLAY_GAME:
RecvPacket_GameState(sGame->multiplayerId,
&sGame->players[sGame->multiplayerId],
&sGame->players[0].comm,
&sGame->players[1].comm,
&sGame->players[2].comm,
&sGame->players[3].comm,
&sGame->players[4].comm,
&sGame->numGraySquares,
&sGame->berriesFalling,
2021-03-01 07:54:51 +01:00
&sGame->allReadyToEnd);
break;
case FUNC_WAIT_END_GAME:
RecvPacket_GameState(sGame->multiplayerId,
&sGame->players[sGame->multiplayerId],
&sGame->players[0].comm,
&sGame->players[1].comm,
&sGame->players[2].comm,
&sGame->players[3].comm,
&sGame->players[4].comm,
&sGame->numGraySquares,
&sGame->berriesFalling,
2021-03-01 07:54:51 +01:00
&sGame->allReadyToEnd);
2019-03-25 23:08:25 +01:00
break;
2019-03-25 22:27:27 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void SendLinkData_Member(void)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
switch (sGame->funcId)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
case FUNC_WAIT_START:
SendPacket_ReadyToStart(TRUE);
sGame->startGame = TRUE;
2019-03-25 23:08:25 +01:00
break;
2021-03-01 07:54:51 +01:00
case FUNC_PLAY_GAME:
if (sGame->player.comm.pickState != PICK_NONE)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
SendPacket_PickState(sGame->player.comm.pickState);
2019-03-25 23:08:25 +01:00
}
break;
2021-03-01 07:54:51 +01:00
case FUNC_WAIT_END_GAME:
if (!sGame->berriesFalling && !sGame->allReadyToEnd)
SendPacket_ReadyToEnd(TRUE);
2019-03-25 23:08:25 +01:00
break;
2019-03-25 22:27:27 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void HandleSound_Leader(void)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
if (sGame->players[sGame->multiplayerId].comm.pickState == PICK_NONE)
2019-03-25 22:27:27 +01:00
{
if (!IsSEPlaying())
2021-03-01 07:54:51 +01:00
sGame->playingPickSound = FALSE;
2019-03-25 22:27:27 +01:00
}
2021-03-01 07:54:51 +01:00
else if (sGame->players[sGame->multiplayerId].comm.ateBerry == TRUE)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
if (!sGame->playingPickSound)
2019-03-25 22:27:27 +01:00
{
2020-08-21 00:02:00 +02:00
m4aSongNumStop(SE_SUCCESS);
PlaySE(SE_SUCCESS);
2021-03-01 07:54:51 +01:00
sGame->playingPickSound = TRUE;
2019-03-25 22:27:27 +01:00
}
}
2021-03-01 07:54:51 +01:00
else if (sGame->players[sGame->multiplayerId].comm.missedBerry == TRUE)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
if (!sGame->playingPickSound && !IsSEPlaying())
2019-03-25 22:27:27 +01:00
{
PlaySE(SE_BOO);
2021-03-01 07:54:51 +01:00
StartDodrioMissedAnim(1);
sGame->playingPickSound = TRUE;
2019-03-25 22:27:27 +01:00
}
}
2021-03-01 07:54:51 +01:00
if (sGame->endSoundState == 0 && sGame->numGraySquares >= NUM_STATUS_SQUARES)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
// Ready to play game over sound
2019-03-25 22:27:27 +01:00
StopMapMusic();
2021-03-01 07:54:51 +01:00
sGame->endSoundState = 1;
2019-03-25 22:27:27 +01:00
}
2021-03-01 07:54:51 +01:00
else if (sGame->endSoundState == 1)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
// Play game over sound
PlayFanfareByFanfareNum(FANFARE_TOO_BAD);
sGame->endSoundState = 2;
2019-03-25 22:27:27 +01:00
}
}
2021-03-01 07:54:51 +01:00
static void HandleSound_Member(void)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
u8 berryStart = sGame->berryColStart;
u8 berryEnd = sGame->berryColEnd;
u8 i;
if (sGame->players[sGame->multiplayerId].comm.pickState == PICK_NONE)
2019-03-25 22:27:27 +01:00
{
if (sGame->players[sGame->multiplayerId].comm.ateBerry != TRUE
2021-03-01 07:54:51 +01:00
&& sGame->players[sGame->multiplayerId].comm.missedBerry != TRUE)
sGame->playingPickSound = 0;
2019-03-25 22:27:27 +01:00
}
2021-03-01 07:54:51 +01:00
else if (sGame->players[sGame->multiplayerId].comm.ateBerry == TRUE)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
if (!sGame->playingPickSound)
2019-03-25 22:27:27 +01:00
{
2020-08-21 00:02:00 +02:00
m4aSongNumStop(SE_SUCCESS);
PlaySE(SE_SUCCESS);
2021-03-01 07:54:51 +01:00
sGame->playingPickSound = TRUE;
2019-03-25 22:27:27 +01:00
}
}
2021-03-01 07:54:51 +01:00
else if (sGame->players[sGame->multiplayerId].comm.missedBerry == TRUE)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
if (!sGame->playingPickSound && !IsSEPlaying())
2019-03-25 22:27:27 +01:00
{
PlaySE(SE_BOO);
2021-03-01 07:54:51 +01:00
StartDodrioMissedAnim(1);
sGame->playingPickSound = TRUE;
2019-03-25 22:27:27 +01:00
}
}
2021-03-01 07:54:51 +01:00
for (i = berryStart; i < berryEnd; i++)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
struct DodrioGame_Berries * berries = &sGame->players[sGame->multiplayerId].berries;
if (berries->fallDist[i] >= MAX_FALL_DIST)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
if (!sGame->playingSquishSound[i])
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
PlaySE(SE_BALLOON_RED + berries->ids[i]);
sGame->playingSquishSound[i] = TRUE;
2019-03-25 22:27:27 +01:00
}
}
else
{
2021-03-01 07:54:51 +01:00
sGame->playingSquishSound[i] = FALSE;
2019-03-25 22:27:27 +01:00
}
}
2021-03-01 07:54:51 +01:00
if (sGame->endSoundState == 0 && sGame->numGraySquares >= NUM_STATUS_SQUARES)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
// Ready to play game over sound
2019-03-25 22:27:27 +01:00
StopMapMusic();
2021-03-01 07:54:51 +01:00
sGame->endSoundState = 1;
2019-03-25 22:27:27 +01:00
}
2021-03-01 07:54:51 +01:00
else if (sGame->endSoundState == 1)
2019-03-25 22:27:27 +01:00
{
2021-03-01 07:54:51 +01:00
// Play game over sound
PlayFanfareByFanfareNum(FANFARE_TOO_BAD);
sGame->endSoundState = 2;
2019-03-25 22:27:27 +01:00
}
}
2019-03-25 23:08:25 +01:00
2021-03-01 07:54:51 +01:00
static void CB2_DodrioGame(void)
2019-03-25 23:08:25 +01:00
{
RunTasks();
AnimateSprites();
BuildOamBuffer();
UpdatePaletteFade();
}
2021-03-01 07:54:51 +01:00
static void VBlankCB_DodrioGame(void)
2019-03-25 23:08:25 +01:00
{
TransferPlttBuffer();
LoadOam();
ProcessSpriteCopyRequests();
}
2022-07-29 17:15:33 +02:00
static void InitMonInfo(struct DodrioGame_MonInfo * monInfo, struct Pokemon *mon)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
monInfo->isShiny = IsMonShiny(mon);
2019-03-25 23:08:25 +01:00
}
2021-03-01 07:54:51 +01:00
static void CreateTask_(TaskFunc func, u8 priority)
2019-03-25 23:08:25 +01:00
{
CreateTask(func, priority);
}
2021-03-01 07:54:51 +01:00
static void CreateDodrioGameTask(TaskFunc func)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->taskId = CreateTask(func, 1);
sGame->state = 0;
sGame->startState = 0;
sGame->timer = 0;
2019-03-25 23:08:25 +01:00
}
2021-03-01 07:54:51 +01:00
static void SetGameFunc(u8 funcId)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->prevFuncId = sGame->funcId;
sGame->funcId = funcId;
sGame->state = 0;
sGame->timer = 0;
2019-03-25 23:08:25 +01:00
}
2021-03-01 07:54:51 +01:00
static bool32 SlideTreeBordersOut(void)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
u8 x = sGame->timer / 4;
sGame->timer++;
if (x != 0 && sGame->timer % 4 == 0)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
if (x < sTreeBorderXPos[sGame->numPlayers - 1])
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
// Update position
SetGpuReg(REG_OFFSET_BG1HOFS, (x * 8)); // BG_TREE_LEFT
SetGpuReg(REG_OFFSET_BG2HOFS, -(x * 8)); // BG_TREE_RIGHT
2019-03-25 23:08:25 +01:00
return FALSE;
}
else
{
2021-03-01 07:54:51 +01:00
// Animation finished
2019-03-25 23:08:25 +01:00
return TRUE;
}
}
else
{
return FALSE;
}
}
2021-03-01 07:54:51 +01:00
static void InitFirstWaveOfBerries(void)
2019-03-25 23:08:25 +01:00
{
u8 i;
2021-03-01 07:54:51 +01:00
u8 berryStart = sGame->berryColStart;
u8 berryEnd = sGame->berryColEnd;
2019-03-25 23:08:25 +01:00
2021-03-01 07:54:51 +01:00
for (i = berryStart; i < berryEnd; i++)
2019-03-25 23:08:25 +01:00
{
2021-03-01 07:54:51 +01:00
struct DodrioGame_Berries * berries = &sGame->player.berries;
berries->fallDist[i] = (i % 2 == 0) ? 1 : 0;
berries->ids[i] = BERRY_BLUE;
2019-03-25 23:08:25 +01:00
}
}
2019-03-26 13:43:49 +01:00
2021-03-01 07:54:51 +01:00
// This function checks every berry and resolves if it should be eaten or not.
// It's run in a loop that handles moving each individual berry, which means
// that every time any berry moves, every single berry is checked.
static void HandlePickBerries(void)
2019-03-26 13:43:49 +01:00
{
2021-03-01 07:54:51 +01:00
u8 berryStart = sGame->berryColStart;
u8 berryEnd = sGame->berryColEnd;
u8 numPlayers = sGame->numPlayers;
u8 i, j, k, column;
2019-03-26 13:43:49 +01:00
2021-03-01 07:54:51 +01:00
// Game is already over
if (sGame->numGraySquares >= NUM_STATUS_SQUARES)
return;
2021-03-01 07:54:51 +01:00
for (i = 0; i < numPlayers; i++)
2019-03-26 13:43:49 +01:00
{
2021-03-01 07:54:51 +01:00
u8 *pickState = &sGame->players[i].comm.pickState;
if (*pickState != PICK_NONE && sGame->inputState[i] == INPUTSTATE_TRY_PICK)
2019-03-26 13:43:49 +01:00
{
2021-03-01 07:54:51 +01:00
// Player is attempting to pick a berry
for (j = berryStart; j < berryEnd; j++)
2019-03-26 13:43:49 +01:00
{
2021-03-01 07:54:51 +01:00
column = sActiveColumnMap[0][0][j];
// Attempt has already been checked
if (sGame->playersAttemptingPick[column][0] == i
2021-03-01 07:54:51 +01:00
|| sGame->playersAttemptingPick[column][1] == i)
break;
2021-03-01 07:54:51 +01:00
// Check berry pick attempt
if (TryPickBerry(i, *pickState, column) == TRUE)
{
2021-03-01 07:54:51 +01:00
// Attempt was successful
for (k = 0; k < ARRAY_COUNT(sGame->playersAttemptingPick[0]); k++)
2019-03-26 13:43:49 +01:00
{
2021-03-01 07:54:51 +01:00
if (sGame->playersAttemptingPick[column][k] == PLAYER_NONE)
2019-03-26 13:43:49 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->playersAttemptingPick[column][k] = i;
sGame->inputState[i] = INPUTSTATE_PICKED;
sGame->berryState[column] = BERRYSTATE_PICKED;
break;
2019-03-26 13:43:49 +01:00
}
}
break;
}
2021-03-01 07:54:51 +01:00
if (sGame->players[i].comm.missedBerry == TRUE)
break;
2019-03-26 13:43:49 +01:00
}
}
}
2021-03-01 07:54:51 +01:00
for (j = berryStart; j < berryEnd; j++)
{
2021-03-01 07:54:51 +01:00
u8 playerIdMissed = PLAYER_NONE;
column = sActiveColumnMap[0][0][j];
if (sGame->berryState[column] == BERRYSTATE_PICKED)
2019-03-26 13:43:49 +01:00
{
2021-03-01 07:54:51 +01:00
s32 delayRemaining;
u8 playerIdPicked, delayStage = sGame->difficulty[GetPlayerIdAtColumn(column)] / NUM_DIFFICULTIES;
if (delayStage >= ARRAY_COUNT(sBerryFallDelays) - 1)
delayStage = ARRAY_COUNT(sBerryFallDelays) - 1;
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
delayRemaining = sBerryFallDelays[delayStage][sGame->players[0].berries.ids[column]] - sGame->fallTimer[column];
if (delayRemaining < 6)
sGame->eatTimer[column] += delayRemaining;
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
if (++sGame->eatTimer[column] >= 6)
{
2021-03-01 07:54:51 +01:00
sGame->eatTimer[column] = 0;
if (sGame->playersAttemptingPick[column][0] == PLAYER_NONE
2021-03-01 07:54:51 +01:00
&& sGame->playersAttemptingPick[column][1] == PLAYER_NONE)
2019-03-26 13:43:49 +01:00
{
2021-03-01 07:54:51 +01:00
// No players attempting to pick this berry
2019-08-02 11:20:15 +02:00
continue;
2019-03-26 13:43:49 +01:00
}
else if (sGame->playersAttemptingPick[column][0] != PLAYER_NONE
2021-03-01 07:54:51 +01:00
&& sGame->playersAttemptingPick[column][1] == PLAYER_NONE)
2019-03-26 13:43:49 +01:00
{
2021-03-01 07:54:51 +01:00
// One player attempting to pick this berry
playerIdPicked = sGame->playersAttemptingPick[column][0];
2019-03-26 13:43:49 +01:00
}
else
2019-03-26 13:43:49 +01:00
{
2021-03-01 07:54:51 +01:00
// Two players attempting to pick this berry
// Randomly give it to one of them
u8 playerId1 = sGame->playersAttemptingPick[column][0];
i = sGame->playersAttemptingPick[column][1]; // playerId2. Have to re-use the variable to match.
2019-08-02 11:20:15 +02:00
if (!(Random() & 1))
2019-03-26 13:43:49 +01:00
{
2021-03-01 07:54:51 +01:00
playerIdPicked = playerId1;
playerIdMissed = i;
2019-03-26 13:43:49 +01:00
}
else
{
2021-03-01 07:54:51 +01:00
playerIdPicked = i;
playerIdMissed = playerId1;
2019-03-26 13:43:49 +01:00
}
}
2021-03-01 07:54:51 +01:00
// Eat berry
sGame->player.berries.fallDist[column] = EAT_FALL_DIST;
sGame->berryState[column] = BERRYSTATE_EATEN;
sGame->inputState[playerIdPicked] = INPUTSTATE_ATE_BERRY;
sGame->berryEatenBy[column] = playerIdPicked;
sGame->players[playerIdPicked].comm.ateBerry = TRUE;
#ifdef UBFIX
if (playerIdMissed != PLAYER_NONE)
#endif
sGame->players[playerIdMissed].comm.missedBerry = TRUE; // UB: playerIdMissed can be PLAYER_NONE here, which is out of bounds
sGame->berriesEaten[playerIdPicked]++;
IncrementBerryResult(0, column, playerIdPicked);
UpdateBerriesPickedInRow(TRUE);
TryIncrementDifficulty(playerIdPicked);
sGame->prevBerryIds[column] = sGame->player.berries.ids[column];
sGame->player.berries.ids[column] = BERRY_MISSED; // Just to clear berry id, wasn't actually missed
sGame->playersAttemptingPick[column][0] = PLAYER_NONE;
sGame->playersAttemptingPick[column][1] = PLAYER_NONE;
2019-03-26 13:43:49 +01:00
}
}
}
}
2019-03-26 16:59:39 +01:00
2021-03-01 07:54:51 +01:00
static bool32 TryPickBerry(u8 playerId, u8 pickState, u8 column)
2019-03-26 16:59:39 +01:00
{
2021-03-01 07:54:51 +01:00
s32 pick = 0;
u8 numPlayersIdx = sGame->numPlayers - 1;
struct DodrioGame_Berries * berries = &sGame->player.berries;
2019-03-26 16:59:39 +01:00
2021-03-01 07:54:51 +01:00
switch (pickState)
2019-03-26 16:59:39 +01:00
{
2021-03-01 07:54:51 +01:00
case PICK_LEFT:
2019-03-26 16:59:39 +01:00
default:
2021-03-01 07:54:51 +01:00
pick = 0;
2019-03-26 16:59:39 +01:00
break;
2021-03-01 07:54:51 +01:00
case PICK_MIDDLE:
pick = 1;
2019-03-26 16:59:39 +01:00
break;
2021-03-01 07:54:51 +01:00
case PICK_RIGHT:
pick = 2;
2019-03-26 16:59:39 +01:00
break;
}
2021-03-01 07:54:51 +01:00
// Check if berry is within range to be picked
if (berries->fallDist[column] == EAT_FALL_DIST - 1 || berries->fallDist[column] == EAT_FALL_DIST)
2019-03-26 16:59:39 +01:00
{
2021-03-01 07:54:51 +01:00
// Check if this berry is the one the player is trying to pick
if (column == sDodrioHeadToColumnMap[numPlayersIdx][playerId][pick])
2019-03-26 16:59:39 +01:00
{
2021-03-01 07:54:51 +01:00
// Check if berry has been picked/eaten by another player
if (sGame->berryState[column] == BERRYSTATE_PICKED || sGame->berryState[column] == BERRYSTATE_EATEN)
2019-03-26 16:59:39 +01:00
{
2021-03-01 07:54:51 +01:00
// Missed berry, picked by someone else
sGame->players[playerId].comm.missedBerry = TRUE;
2019-03-26 16:59:39 +01:00
return FALSE;
}
else
{
2021-03-01 07:54:51 +01:00
// Successfully picked berry
2019-03-26 16:59:39 +01:00
return TRUE;
}
}
}
else
{
2021-03-01 07:54:51 +01:00
// Check if this berry is the one the player is trying to pick
if (column == sDodrioHeadToColumnMap[numPlayersIdx][playerId][pick])
2019-03-26 16:59:39 +01:00
{
2021-03-01 07:54:51 +01:00
// Missed berry, out of range
sGame->inputState[playerId] = INPUTSTATE_BAD_MISS;
sGame->players[playerId].comm.missedBerry = TRUE;
2019-03-26 16:59:39 +01:00
}
}
return FALSE;
}
2019-03-26 19:46:58 +01:00
2021-03-01 07:54:51 +01:00
static void UpdateFallingBerries(void)
2019-03-26 19:46:58 +01:00
{
2021-03-01 07:54:51 +01:00
u8 berryStart = sGame->berryColStart;
u8 berryEnd = sGame->berryColEnd;
u8 delayStage = 0;
u8 otherBerryMissed = 0;
2019-03-26 19:46:58 +01:00
u8 i;
2021-03-01 07:54:51 +01:00
sGame->berriesFalling = FALSE;
2019-03-26 19:46:58 +01:00
2021-03-01 07:54:51 +01:00
for (i = berryStart; i < berryEnd - 1; i++)
2019-03-26 19:46:58 +01:00
{
2021-03-01 07:54:51 +01:00
struct DodrioGame *game = sGame;
2019-03-26 19:46:58 +01:00
2021-03-01 07:54:51 +01:00
if (sGame->berryState[i] == BERRYSTATE_NONE || sGame->berryState[i] == BERRYSTATE_PICKED)
2019-03-26 19:46:58 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->berriesFalling = TRUE;
2021-03-01 07:54:51 +01:00
if (game->player.berries.fallDist[i] >= MAX_FALL_DIST)
2019-03-26 19:46:58 +01:00
{
2021-03-01 07:54:51 +01:00
// Berry hit the ground
game->player.berries.fallDist[i] = MAX_FALL_DIST;
sGame->berryState[i] = BERRYSTATE_SQUISHED;
if (!sGame->playingSquishSound[i])
2019-03-26 19:46:58 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->playingSquishSound[i] = TRUE;
PlaySE(SE_BALLOON_RED + game->player.berries.ids[i]);
2019-03-26 19:46:58 +01:00
}
2021-03-01 07:54:51 +01:00
if (sGame->numGraySquares < NUM_STATUS_SQUARES || otherBerryMissed == TRUE)
2019-03-26 19:46:58 +01:00
{
2021-03-01 07:54:51 +01:00
otherBerryMissed = TRUE;
sGame->playingSquishSound[i] = FALSE;
if (sGame->numGraySquares < NUM_STATUS_SQUARES)
sGame->numGraySquares++;
IncrementBerryResult(BERRY_MISSED, i, 0);
UpdateBerriesPickedInRow(FALSE);
2019-03-26 19:46:58 +01:00
}
}
else
{
2021-03-01 07:54:51 +01:00
// Berry is still falling
u8 delay;
delayStage = sGame->difficulty[GetPlayerIdAtColumn(i)] / NUM_DIFFICULTIES;
if (delayStage >= ARRAY_COUNT(sBerryFallDelays) - 1)
delayStage = ARRAY_COUNT(sBerryFallDelays) - 1;
delay = sBerryFallDelays[delayStage][game->player.berries.ids[i]];
if (++sGame->fallTimer[i] >= delay)
2019-03-26 19:46:58 +01:00
{
2021-03-01 07:54:51 +01:00
game->player.berries.fallDist[i]++;
sGame->fallTimer[i] = 0;
2019-03-26 19:46:58 +01:00
}
2021-03-01 07:54:51 +01:00
HandlePickBerries();
2019-03-26 19:46:58 +01:00
}
}
2021-03-01 07:54:51 +01:00
else if (sGame->berryState[i] == BERRYSTATE_EATEN)
2019-03-26 19:46:58 +01:00
{
2021-03-01 07:54:51 +01:00
// Berry has been eaten, wait and create a new berry
sGame->berriesFalling = TRUE;
if (++sGame->newBerryTimer[i] >= 20)
2019-03-26 19:46:58 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->players[sGame->berryEatenBy[i]].comm.ateBerry = FALSE;
sGame->newBerryTimer[i] = 0;
sGame->fallTimer[i] = 0;
sGame->berryState[i] = BERRYSTATE_NONE;
game->player.berries.fallDist[i] = 1;
game->player.berries.ids[i] = GetNewBerryId(GetPlayerIdAtColumn(i), i);
2019-03-26 19:46:58 +01:00
}
}
2021-03-01 07:54:51 +01:00
else if (sGame->berryState[i] == BERRYSTATE_SQUISHED)
2019-03-26 19:46:58 +01:00
{
2021-03-01 07:54:51 +01:00
// Berry has already hit the ground, wait and create a new berry
if (++sGame->newBerryTimer[i] >= 20)
2019-03-26 19:46:58 +01:00
{
2021-03-01 07:54:51 +01:00
if (sGame->numGraySquares < NUM_STATUS_SQUARES)
2019-03-26 19:46:58 +01:00
{
2021-03-01 07:54:51 +01:00
sGame->newBerryTimer[i] = 0;
sGame->fallTimer[i] = 0;
sGame->berryState[i] = BERRYSTATE_NONE;
game->player.berries.fallDist[i] = 1;
sGame->prevBerryIds[i] = game->player.berries.ids[i];
game->player.berries.ids[i] = GetNewBerryId(GetPlayerIdAtColumn(i), i);
2019-03-26 19:46:58 +01:00
}
}
}
}
}
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
static void UpdateBerrySprites(void)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i;
u8 berryStart = sGame->berryColStart;
u8 berryEnd = sGame->berryColEnd;
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
for (i = berryStart; i < berryEnd; i++)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
struct DodrioGame_Player *player = &sGame->players[sGame->multiplayerId];
u8 column = sActiveColumnMap[sGame->numPlayers - 1][sGame->multiplayerId][i];
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
if (player->berries.fallDist[column] != 0)
SetBerryInvisibility(i, FALSE);
2019-07-31 00:27:30 +02:00
else
2021-03-01 07:54:51 +01:00
SetBerryInvisibility(i, TRUE);
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
if (player->berries.fallDist[column] >= MAX_FALL_DIST)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
// Berry was missed, set squished anim
SetBerryAnim(i, player->berries.ids[column] + BERRY_MISSED);
SetBerryYPos(i, player->berries.fallDist[column] * 2 - 1);
2019-07-31 00:27:30 +02:00
}
2021-03-01 07:54:51 +01:00
else if (player->berries.ids[column] == 3)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
// Berry was picked, set eaten anim
player->berries.fallDist[column] = EAT_FALL_DIST;
SetBerryAnim(i, ANIM_EATEN);
SetBerryYPos(i, player->berries.fallDist[column] * 2 - 1);
2019-07-31 00:27:30 +02:00
}
else
{
2021-03-01 07:54:51 +01:00
// Berry is still falling
SetBerryAnim(i, player->berries.ids[column]);
SetBerryYPos(i, player->berries.fallDist[column] * 2);
2019-07-31 00:27:30 +02:00
}
}
}
2021-03-01 07:54:51 +01:00
static void UpdateAllDodrioAnims(void)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, numPlayers;
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
numPlayers = sGame->numPlayers;
for (i = 0; i < numPlayers; i++)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
struct DodrioGame_Player *player = &sGame->players[i];
SetDodrioAnim(i, player->comm.pickState);
2019-07-31 00:27:30 +02:00
}
}
2021-03-01 07:54:51 +01:00
static void SetAllDodrioDisabled(void)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, numPlayers;
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
numPlayers = sGame->numPlayers;
for (i = 0; i < numPlayers; i++)
SetDodrioAnim(i, PICK_DISABLED);
2019-07-31 00:27:30 +02:00
}
2021-03-01 07:54:51 +01:00
static void UpdateGame_Leader(void)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
UpdateBerrySprites();
if (sGame->numGraySquares >= NUM_STATUS_SQUARES)
SetAllDodrioDisabled();
2019-07-31 00:27:30 +02:00
else
2021-03-01 07:54:51 +01:00
UpdateAllDodrioAnims();
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
UpdateStatusBarAnim(sGame->numGraySquares);
2019-07-31 00:27:30 +02:00
}
2021-03-01 07:54:51 +01:00
// Identical to UpdateGame_Leader
static void UpdateGame_Member(void)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
UpdateBerrySprites();
if (sGame->numGraySquares >= NUM_STATUS_SQUARES)
SetAllDodrioDisabled();
2019-07-31 00:27:30 +02:00
else
2021-03-01 07:54:51 +01:00
UpdateAllDodrioAnims();
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
UpdateStatusBarAnim(sGame->numGraySquares);
2019-07-31 00:27:30 +02:00
}
2021-03-01 07:54:51 +01:00
static void GetActiveBerryColumns(u8 numPlayers, u8 *start, u8 *end)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
switch (numPlayers)
2019-07-31 00:27:30 +02:00
{
case 1:
2021-03-01 07:54:51 +01:00
*start = 4, *end = 7;
2019-07-31 00:27:30 +02:00
break;
case 2:
2021-03-01 07:54:51 +01:00
*start = 3, *end = 8;
2019-07-31 00:27:30 +02:00
break;
case 3:
2021-03-01 07:54:51 +01:00
*start = 2, *end = 9;
2019-07-31 00:27:30 +02:00
break;
case 4:
2021-03-01 07:54:51 +01:00
*start = 1, *end = 10;
2019-07-31 00:27:30 +02:00
break;
case 5:
2021-03-01 07:54:51 +01:00
*start = 0, *end = 11;
2019-07-31 00:27:30 +02:00
break;
}
}
2021-03-01 07:54:51 +01:00
static bool32 AllPlayersReadyToStart(void)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, numPlayers;
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
numPlayers = sGame->numPlayers;
for (i = 1; i < numPlayers; i++)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
if (sGame->readyToStart[i] == FALSE)
sGame->readyToStart[i] = RecvPacket_ReadyToStart(i);
2019-07-31 00:27:30 +02:00
}
2021-03-01 07:54:51 +01:00
numPlayers = numPlayers; // Needed to force compiler to keep loop below
#ifdef BUGFIX
i = 1; // i isn't reset, loop below never runs. As a result, game can begin before all players ready
#endif
for (; i < numPlayers; i++)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
if (sGame->readyToStart[i] == FALSE)
2019-07-31 00:27:30 +02:00
return FALSE;
}
return TRUE;
}
2021-03-01 07:54:51 +01:00
static void ResetReadyToStart(void)
2019-07-31 00:27:30 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < MAX_RFU_PLAYERS; i++)
sGame->readyToStart[i] = FALSE;
2019-07-31 00:27:30 +02:00
}
2021-03-01 07:54:51 +01:00
static bool32 ReadyToEndGame_Leader(void)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
if (sGame->numGraySquares >= NUM_STATUS_SQUARES && !sGame->berriesFalling)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
sGame->numGraySquares = NUM_STATUS_SQUARES;
if (sGame->allReadyToEnd)
2019-07-31 00:27:30 +02:00
return TRUE;
}
return FALSE;
}
2021-03-01 07:54:51 +01:00
static bool32 ReadyToEndGame_Member(void)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, berryStart, berryEnd;
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
if (sGame->numGraySquares >= NUM_STATUS_SQUARES)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
berryStart = sGame->berryColStart;
berryEnd = sGame->berryColEnd;
sGame->numGraySquares = NUM_STATUS_SQUARES;
if (sGame->allReadyToEnd)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
for (i = berryStart; i < berryEnd; i++)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
struct DodrioGame_Player *player = &sGame->players[sGame->multiplayerId];
u8 column = sActiveColumnMap[sGame->numPlayers - 1][sGame->multiplayerId][i];
2019-07-31 00:27:30 +02:00
2021-03-01 07:54:51 +01:00
if (player->berries.fallDist[column] != MAX_FALL_DIST)
2019-07-31 00:27:30 +02:00
return FALSE;
}
return TRUE;
}
}
return FALSE;
}
2021-03-01 07:54:51 +01:00
static void TryIncrementDifficulty(u8 playerId)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
u8 threshold = sDifficultyThresholds[sGame->difficulty[playerId] % NUM_DIFFICULTIES] + (sGame->difficulty[playerId] / NUM_DIFFICULTIES) * 100;
if (sGame->berriesEaten[playerId] >= threshold)
sGame->difficulty[playerId]++;
2019-07-31 00:27:30 +02:00
}
2021-03-01 07:54:51 +01:00
static u8 GetPlayerIdAtColumn(u8 column)
2019-07-31 00:27:30 +02:00
{
2021-03-01 07:54:51 +01:00
return sPlayerIdAtColumn[sGame->numPlayers - 1][column];
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
// Get a berry id for when a new falling berry is created.
// What type of berry it is depends on the current difficulty
// level of players who can pick berries from that column.
static u8 GetNewBerryId(u8 playerId, u8 column)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, highestDifficulty;
u8 numPlayersIdx = sGame->numPlayers - 1;
u8 leftPlayer = sDodrioNeighborMap[numPlayersIdx][playerId][0];
u8 middlePlayer = sDodrioNeighborMap[numPlayersIdx][playerId][1];
u8 rightPlayer = sDodrioNeighborMap[numPlayersIdx][playerId][2];
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
for (i = 0; sUnsharedColumns[numPlayersIdx][i] != 0; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
// If only one player can use this column, just use their difficulty
if (column == sUnsharedColumns[numPlayersIdx][i])
return GetNewBerryIdByDifficulty(sGame->difficulty[middlePlayer], column);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
// This column is shared, get the highest difficulty of adjacent players
if (sGame->difficulty[leftPlayer] > sGame->difficulty[middlePlayer])
highestDifficulty = sGame->difficulty[leftPlayer];
2019-08-01 19:01:50 +02:00
else
2021-03-01 07:54:51 +01:00
highestDifficulty = sGame->difficulty[middlePlayer];
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
if (sGame->difficulty[rightPlayer] > highestDifficulty)
highestDifficulty = sGame->difficulty[rightPlayer];
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
return GetNewBerryIdByDifficulty(highestDifficulty, column);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
// The berry types cycle through different distributions depending on the difficulty
static u8 GetNewBerryIdByDifficulty(u8 difficulty, u8 column)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 prevBerryId = sGame->prevBerryIds[column];
switch (difficulty % NUM_DIFFICULTIES)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
default: return BERRY_BLUE;
case 0: return BERRY_BLUE;
case 1: return BERRY_GREEN;
case 2: return BERRY_GOLD;
2019-08-01 19:01:50 +02:00
case 3:
2021-03-01 07:54:51 +01:00
if (prevBerryId == BERRY_BLUE)
return BERRY_GREEN;
2019-08-01 19:01:50 +02:00
else
2021-03-01 07:54:51 +01:00
return BERRY_BLUE;
2019-08-01 19:01:50 +02:00
case 4:
2021-03-01 07:54:51 +01:00
if (prevBerryId == BERRY_BLUE)
return BERRY_GOLD;
2019-08-01 19:01:50 +02:00
else
2021-03-01 07:54:51 +01:00
return BERRY_BLUE;
2019-08-01 19:01:50 +02:00
case 5:
2021-03-01 07:54:51 +01:00
if (prevBerryId == BERRY_GOLD)
return BERRY_GREEN;
2019-08-01 19:01:50 +02:00
else
2021-03-01 07:54:51 +01:00
return BERRY_GOLD;
2019-08-01 19:01:50 +02:00
case 6:
2021-03-01 07:54:51 +01:00
if (prevBerryId == BERRY_BLUE)
return BERRY_GREEN;
else if (prevBerryId == BERRY_GREEN)
return BERRY_GOLD;
2019-08-01 19:01:50 +02:00
else
2021-03-01 07:54:51 +01:00
return BERRY_BLUE;
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
static bool32 IsTotalBerriesMissedOver10(u16 berryResults[MAX_RFU_PLAYERS][NUM_BERRY_IDS])
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
int missed = 0, i = 0;
for (; i < GetLinkPlayerCount(); missed += berryResults[i][BERRY_MISSED], i++)
2019-08-01 19:01:50 +02:00
;
2021-03-01 07:54:51 +01:00
if (missed > 10)
2019-08-01 19:01:50 +02:00
return TRUE;
else
return FALSE;
}
2021-03-01 07:54:51 +01:00
// Despite being set up to take a berry id as an argument, this
// function is only ever given BERRY_BLUE or BERRY_MISSED.
// It reads the actual berry id (if necessary) from ids
static void IncrementBerryResult(u8 berryIdArg, u8 column, u8 playerId)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 berryId;
u8 numPlayers = sGame->numPlayers;
switch (berryIdArg)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
case BERRY_BLUE:
case BERRY_GREEN:
case BERRY_GOLD:
berryId = sGame->players[0].berries.ids[column];
sGame->berryResults[playerId][berryId] = IncrementWithLimit(sGame->berryResults[playerId][berryId], 20000);
2019-08-01 19:01:50 +02:00
break;
2021-03-01 07:54:51 +01:00
case BERRY_MISSED:
if (IsTotalBerriesMissedOver10(sGame->berryResults))
2019-08-01 19:01:50 +02:00
break;
2021-03-01 07:54:51 +01:00
switch (numPlayers)
2019-08-01 19:01:50 +02:00
{
case 5:
2021-03-01 07:54:51 +01:00
switch (column)
2019-08-01 19:01:50 +02:00
{
case 0:
2021-03-01 07:54:51 +01:00
sGame->berryResults[2][BERRY_MISSED]++;
sGame->berryResults[3][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 1:
2021-03-01 07:54:51 +01:00
sGame->berryResults[3][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 2:
2021-03-01 07:54:51 +01:00
sGame->berryResults[3][BERRY_MISSED]++;
sGame->berryResults[4][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 3:
2021-03-01 07:54:51 +01:00
sGame->berryResults[4][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 4:
2021-03-01 07:54:51 +01:00
sGame->berryResults[4][BERRY_MISSED]++;
sGame->berryResults[0][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 5:
2021-03-01 07:54:51 +01:00
sGame->berryResults[0][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 6:
2021-03-01 07:54:51 +01:00
sGame->berryResults[0][BERRY_MISSED]++;
sGame->berryResults[1][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 7:
2021-03-01 07:54:51 +01:00
sGame->berryResults[1][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 8:
2021-03-01 07:54:51 +01:00
sGame->berryResults[1][BERRY_MISSED]++;
sGame->berryResults[2][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 9:
2021-03-01 07:54:51 +01:00
sGame->berryResults[2][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
}
break;
case 4:
2021-03-01 07:54:51 +01:00
switch (column)
2019-08-01 19:01:50 +02:00
{
case 1:
2021-03-01 07:54:51 +01:00
sGame->berryResults[2][BERRY_MISSED]++;
sGame->berryResults[3][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 2:
2021-03-01 07:54:51 +01:00
sGame->berryResults[3][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 3:
2021-03-01 07:54:51 +01:00
sGame->berryResults[3][BERRY_MISSED]++;
sGame->berryResults[0][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 4:
2021-03-01 07:54:51 +01:00
sGame->berryResults[0][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 5:
2021-03-01 07:54:51 +01:00
sGame->berryResults[0][BERRY_MISSED]++;
sGame->berryResults[1][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 6:
2021-03-01 07:54:51 +01:00
sGame->berryResults[1][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 7:
2021-03-01 07:54:51 +01:00
sGame->berryResults[1][BERRY_MISSED]++;
sGame->berryResults[2][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 8:
2021-03-01 07:54:51 +01:00
sGame->berryResults[2][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
}
break;
case 3:
2021-03-01 07:54:51 +01:00
switch (column)
2019-08-01 19:01:50 +02:00
{
case 2:
2021-03-01 07:54:51 +01:00
sGame->berryResults[1][BERRY_MISSED]++;
sGame->berryResults[2][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 3:
2021-03-01 07:54:51 +01:00
sGame->berryResults[2][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 4:
2021-03-01 07:54:51 +01:00
sGame->berryResults[2][BERRY_MISSED]++;
sGame->berryResults[0][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 5:
2021-03-01 07:54:51 +01:00
sGame->berryResults[0][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 6:
2021-03-01 07:54:51 +01:00
sGame->berryResults[0][BERRY_MISSED]++;
sGame->berryResults[1][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 7:
2021-03-01 07:54:51 +01:00
sGame->berryResults[1][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
}
break;
case 2:
2021-03-01 07:54:51 +01:00
switch (column)
2019-08-01 19:01:50 +02:00
{
case 3:
2021-03-01 07:54:51 +01:00
sGame->berryResults[0][BERRY_MISSED]++;
sGame->berryResults[1][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 4:
2021-03-01 07:54:51 +01:00
sGame->berryResults[0][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 5:
2021-03-01 07:54:51 +01:00
sGame->berryResults[0][BERRY_MISSED]++;
sGame->berryResults[1][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
case 6:
2021-03-01 07:54:51 +01:00
sGame->berryResults[1][BERRY_MISSED]++;
2019-08-01 19:01:50 +02:00
break;
}
break;
}
break;
}
}
2021-03-01 07:54:51 +01:00
static void UpdateBerriesPickedInRow(bool32 picked)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
// The 'berries picked in row' stat is only
// counted for games with all 5 players
if (sGame->numPlayers != MAX_RFU_PLAYERS)
2019-08-01 19:01:50 +02:00
return;
2021-03-01 07:54:51 +01:00
if (picked == TRUE)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
if (++sGame->berriesPickedInRow > sGame->maxBerriesPickedInRow)
sGame->maxBerriesPickedInRow = sGame->berriesPickedInRow;
if (sGame->berriesPickedInRow > MAX_BERRIES)
sGame->berriesPickedInRow = MAX_BERRIES;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
else // missed
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
if (sGame->berriesPickedInRow > sGame->maxBerriesPickedInRow)
sGame->maxBerriesPickedInRow = sGame->berriesPickedInRow;
sGame->berriesPickedInRow = 0;
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
static void SetMaxBerriesPickedInRow(void)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < sGame->numPlayers; i++)
sGame->berryResults[i][BERRY_IN_ROW] = sGame->maxBerriesPickedInRow;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void ResetForPlayAgainPrompt(void)
2019-08-01 19:01:50 +02:00
{
u8 i, j;
2021-03-01 07:54:51 +01:00
for (i = 0; i < MAX_RFU_PLAYERS; i++)
{
for (j = 0; j < NUM_BERRY_COLUMNS; j++)
sGame->players[i].berries.fallDist[j] = 0;
sGame->players[i].comm.pickState = PICK_NONE;
sGame->players[i].comm.ateBerry = FALSE;
sGame->difficulty[i] = 0;
sGame->berriesEaten[i] = 0;
sGame->scoreResults[i].ranking = 0;
sGame->scoreResults[i].score = 0;
sGame->berryResults[i][BERRY_BLUE] = 0;
sGame->berryResults[i][BERRY_GREEN] = 0;
sGame->berryResults[i][BERRY_GOLD] = 0;
sGame->berryResults[i][BERRY_MISSED] = 0;
sGame->berryResults[i][BERRY_PRIZE] = 0;
sGame->berryResults[i][BERRY_IN_ROW] = 0;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
sGame->endSoundState = 0;
sGame->berriesPickedInRow = 0;
sGame->numGraySquares = 0;
UpdateAllDodrioAnims();
UpdateBerrySprites();
}
static const s16 sBerryScoreMultipliers[] = {
[BERRY_BLUE] = 10,
[BERRY_GREEN] = 30,
[BERRY_GOLD] = 50,
2021-03-01 07:54:51 +01:00
[BERRY_MISSED] = 50 // Subtracted
};
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
static void SetRandomPrize(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, prizeSet = 0, prizeIdx = 0;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
switch (sGame->numPlayers)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
case 4: prizeSet = 1; break;
case 5: prizeSet = 2; break;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
prizeIdx = Random() % ARRAY_COUNT(sPrizeBerryIds[0]);
for (i = 0; i < MAX_RFU_PLAYERS; i++)
sGame->berryResults[i][BERRY_PRIZE] = sPrizeBerryIds[prizeSet][prizeIdx];
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static u32 GetBerriesPicked(u8 playerId)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u32 sum = sGame->berryResults[playerId][BERRY_BLUE]
+ sGame->berryResults[playerId][BERRY_GREEN]
+ sGame->berryResults[playerId][BERRY_GOLD];
return min(sum, MAX_BERRIES);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void TryUpdateRecords(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u32 berriesPicked = Min(GetBerriesPicked(sGame->multiplayerId), MAX_BERRIES); // Min here is redundant
u32 score = Min(GetScore(sGame->multiplayerId), MAX_SCORE);
2019-08-01 19:01:50 +02:00
if (gSaveBlock2Ptr->berryPick.bestScore < score)
gSaveBlock2Ptr->berryPick.bestScore = score;
if (gSaveBlock2Ptr->berryPick.berriesPicked < berriesPicked)
gSaveBlock2Ptr->berryPick.berriesPicked = berriesPicked;
2021-03-01 07:54:51 +01:00
if (gSaveBlock2Ptr->berryPick.berriesPickedInRow < sGame->maxBerriesPickedInRow)
gSaveBlock2Ptr->berryPick.berriesPickedInRow = sGame->maxBerriesPickedInRow;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
// Enqueue the given state, and dequeue and return
// the state that should be used next
static u8 UpdatePickStateQueue(u8 pickState)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, nextState;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
nextState = sGame->pickStateQueue[ARRAY_COUNT(sGame->pickStateQueue) - 1];
for (i = ARRAY_COUNT(sGame->pickStateQueue) - 1; i != 0; i--)
sGame->pickStateQueue[i] = sGame->pickStateQueue[i - 1];
sGame->pickStateQueue[0] = pickState;
return nextState;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
// The player may extend their Dodrio's heads while they wait for
// other players to respond to the "Play again?" prompt
static void HandleWaitPlayAgainInput(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
if (sGame->inputDelay[sGame->multiplayerId] == 0)
2019-08-01 19:01:50 +02:00
{
2020-09-05 03:11:55 +02:00
if (JOY_NEW(DPAD_UP))
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sGame->players[sGame->multiplayerId].comm.pickState = PICK_MIDDLE;
sGame->inputDelay[sGame->multiplayerId] = 6;
2020-08-21 00:02:00 +02:00
PlaySE(SE_M_CHARM);
2019-08-01 19:01:50 +02:00
}
2020-09-05 03:11:55 +02:00
else if (JOY_NEW(DPAD_LEFT))
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sGame->players[sGame->multiplayerId].comm.pickState = PICK_LEFT;
sGame->inputDelay[sGame->multiplayerId] = 6;
2020-08-21 00:02:00 +02:00
PlaySE(SE_M_CHARM);
2019-08-01 19:01:50 +02:00
}
2020-09-05 03:11:55 +02:00
else if (JOY_NEW(DPAD_RIGHT))
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sGame->players[sGame->multiplayerId].comm.pickState = PICK_RIGHT;
sGame->inputDelay[sGame->multiplayerId] = 6;
2020-08-21 00:02:00 +02:00
PlaySE(SE_M_CHARM);
2019-08-01 19:01:50 +02:00
}
else
{
2021-03-01 07:54:51 +01:00
sGame->players[sGame->multiplayerId].comm.pickState = PICK_NONE;
2019-08-01 19:01:50 +02:00
}
}
else
{
2021-03-01 07:54:51 +01:00
sGame->inputDelay[sGame->multiplayerId]--;
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
static void ResetPickState(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sGame->players[sGame->multiplayerId].comm.pickState = PICK_NONE;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static u16 GetPrizeItemId(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
return sGame->berryResults[sGame->multiplayerId][BERRY_PRIZE] + FIRST_BERRY_INDEX;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static u8 GetNumPlayers(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
return sGame->numPlayers;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static u8 *GetPlayerName(u8 id)
2019-08-01 19:01:50 +02:00
{
if (gReceivedRemoteLinkPlayers)
return gLinkPlayers[id].name;
else
2021-03-01 07:54:51 +01:00
return sGame->players[id].name;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static u16 GetBerryResult(u8 playerId, u8 berryId)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
return sGame->berryResults[playerId][berryId];
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static u32 GetScore(u8 playerId)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
u32 scoreLost, score = 0;
// Sum up points for berries picked
for (i = 0; i < BERRY_MISSED; i++)
score += sGame->berryResults[playerId][i] * sBerryScoreMultipliers[i];
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
// Get points lost for berries missed
scoreLost = sGame->berryResults[playerId][BERRY_MISSED] * sBerryScoreMultipliers[BERRY_MISSED];
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
if (score <= scoreLost)
2019-08-01 19:01:50 +02:00
return 0;
else
2021-03-01 07:54:51 +01:00
return score - scoreLost;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static u32 GetHighestScore(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, numPlayers = sGame->numPlayers;
u32 maxScore = GetScore(0);
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
for (i = 1; i < numPlayers; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u32 score = GetScore(i);
if (score > maxScore)
maxScore = score;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
return Min(maxScore, MAX_SCORE);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static u32 GetHighestBerryResult(u8 berryId)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, numPlayers = sGame->numPlayers;
u16 maxScore = sGame->berryResults[0][berryId];
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
for (i = 0; i < numPlayers; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u16 score = sGame->berryResults[i][berryId];
if (score > maxScore)
maxScore = score;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
return maxScore;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static u32 GetScoreByRanking(u8 ranking)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u32 scores[MAX_RFU_PLAYERS], temp;
s16 unsorted = TRUE;
u8 i;
u8 numPlayers = sGame->numPlayers;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
for (i = 0; i < numPlayers; i++)
scores[i] = temp = GetScore(i);
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
// Sort the scores in the array highest to lowest
while (unsorted)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
unsorted = FALSE;
for (i = 0; i < numPlayers - 1; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
if (scores[i] < scores[i + 1])
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
SWAP(scores[i], scores[i + 1], temp);
unsorted = TRUE;
2019-08-01 19:01:50 +02:00
}
}
}
2021-03-01 07:54:51 +01:00
return scores[ranking];
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static u32 SetScoreResults(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, ranking = 0, nextRanking = 0, playersRanked = 0;
u8 numPlayers = sGame->numPlayers;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
GetHighestScore(); // Useless call
2021-03-01 07:54:51 +01:00
if (GetHighestScore() == 0)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
// No one scored any points, put everyone in last place with a score of 0.
// Presumably this was supposed to then return, as the assignments in this
// loop are then overwritten by the rest of the function
for (i = 0; i < numPlayers; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sGame->scoreResults[i].ranking = MAX_RFU_PLAYERS - 1;
sGame->scoreResults[i].score = 0;
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
// Set scores
for (i = 0; i < numPlayers; i++)
sGame->scoreResults[i].score = Min(GetScore(i), MAX_SCORE);
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
// Set rankings
2019-08-01 19:01:50 +02:00
do
{
2021-03-01 07:54:51 +01:00
u32 score = GetScoreByRanking(ranking);
u8 curRanking = nextRanking;
2021-03-01 07:54:51 +01:00
// Find all players with the score for this ranking.
// Increment nextRanking but not curRanking to allow
// for ties
for (i = 0; i < numPlayers; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
if (score == sGame->scoreResults[i].score)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sGame->scoreResults[i].ranking = curRanking;
nextRanking++;
playersRanked++;
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
ranking = nextRanking;
} while (playersRanked < numPlayers);
2019-08-01 19:01:50 +02:00
return 0;
}
2021-03-01 07:54:51 +01:00
static void GetScoreResults(struct DodrioGame_ScoreResults *dst, u8 playerId)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
*dst = sGame->scoreResults[playerId];
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
// Unused
// Returns where the specified player's score ranks, 0 being first (highest score)
static u8 GetScoreRanking(u8 playerId)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, ranking = 0;
u8 numPlayers = sGame->numPlayers;
u32 playersScore;
u32 scores[MAX_RFU_PLAYERS] = {0};
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
for (i = 0; i < numPlayers; i++)
scores[i] = GetScore(i);
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
playersScore = scores[playerId];
for (i = 0; i < MAX_RFU_PLAYERS; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
if (i != playerId && playersScore < scores[i])
ranking++;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
return ranking;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
enum {
PRIZE_RECEIVED,
PRIZE_FILLED_BAG,
PRIZE_NO_ROOM,
NO_PRIZE,
};
static u8 TryGivePrize(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 multiplayerId = sGame->multiplayerId;
u16 itemId = GetPrizeItemId();
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
if (GetScore(multiplayerId) != GetHighestScore())
return NO_PRIZE;
2019-08-01 19:01:50 +02:00
if (!CheckBagHasSpace(itemId, 1))
2021-03-01 07:54:51 +01:00
return PRIZE_NO_ROOM;
2019-08-01 19:01:50 +02:00
AddBagItem(itemId, 1);
if (!CheckBagHasSpace(itemId, 1))
2021-03-01 07:54:51 +01:00
return PRIZE_FILLED_BAG;
return PRIZE_RECEIVED;
2019-08-01 19:01:50 +02:00
}
2022-06-01 18:41:57 +02:00
static u32 IncrementWithLimit(u32 num, u32 max)
2019-08-01 19:01:50 +02:00
{
2022-06-01 18:41:57 +02:00
if (num < max)
return num + 1;
2019-08-01 19:01:50 +02:00
else
return max;
}
2019-08-02 00:41:55 +02:00
static u32 Min(u32 a, u32 b)
2019-08-01 19:01:50 +02:00
{
if (a < b)
return a;
else
return b;
}
2021-03-01 07:54:51 +01:00
static u8 GetPlayerIdByPos(u8 id)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
return sGame->posToPlayerId[id];
2019-08-01 19:01:50 +02:00
}
2019-10-12 04:23:05 +02:00
void IsDodrioInParty(void)
2019-08-01 19:01:50 +02:00
{
int i;
for (i = 0; i < PARTY_SIZE; i++)
{
if (GetMonData(&gPlayerParty[i], MON_DATA_SANITY_HAS_SPECIES)
&& GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2) == SPECIES_DODRIO)
{
2019-10-11 10:14:09 +02:00
gSpecialVar_Result = TRUE;
2019-08-01 19:01:50 +02:00
return;
}
}
2019-10-11 10:14:09 +02:00
gSpecialVar_Result = FALSE;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
#define NUM_RECORD_TYPES 3
2019-10-11 10:14:09 +02:00
void ShowDodrioBerryPickingRecords(void)
2019-08-01 19:01:50 +02:00
{
2019-10-11 10:14:09 +02:00
u8 taskId = CreateTask(Task_ShowDodrioBerryPickingRecords, 0);
Task_ShowDodrioBerryPickingRecords(taskId);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static const struct WindowTemplate sWindowTemplates_Records =
2019-08-01 19:01:50 +02:00
{
.bg = 0,
.tilemapLeft = 5,
.tilemapTop = 1,
.width = 20,
.height = 11,
.paletteNum = 15,
.baseBlock = 0x1,
};
2021-03-01 07:54:51 +01:00
static const u8 *const sRecordsTexts[NUM_RECORD_TYPES] = {gText_BerriesPicked, gText_BestScore, gText_BerriesInRowFivePlayers};
static const u8 sRecordNumMaxDigits[NUM_RECORD_TYPES] = {4, 7, 4};
2019-08-01 19:01:50 +02:00
ALIGNED(4)
2021-03-01 07:54:51 +01:00
static const u8 sRecordTextYCoords[NUM_RECORD_TYPES][2] = {{25}, {41}, {57}};
static const u8 sRecordNumYCoords[NUM_RECORD_TYPES][2] = {{25}, {41}, {73}};
#define tState data[0]
#define tWindowId data[1]
2019-08-01 19:01:50 +02:00
2019-10-11 10:14:09 +02:00
static void Task_ShowDodrioBerryPickingRecords(u8 taskId)
2019-08-01 19:01:50 +02:00
{
struct WindowTemplate window;
s32 i, width, widthCurr;
s16 *data = gTasks[taskId].data;
2021-03-01 07:54:51 +01:00
switch (tState)
2019-08-01 19:01:50 +02:00
{
case 0:
2021-03-01 07:54:51 +01:00
window = sWindowTemplates_Records;
2021-10-30 22:47:37 +02:00
width = GetStringWidth(FONT_NORMAL, gText_BerryPickingRecords, 0);
2021-03-01 07:54:51 +01:00
for (i = 0; i < ARRAY_COUNT(sRecordsTexts); i++)
2019-08-01 19:01:50 +02:00
{
2021-10-30 22:47:37 +02:00
widthCurr = GetStringWidth(FONT_NORMAL, sRecordsTexts[i], 0) + 50;
2019-08-01 19:01:50 +02:00
if (widthCurr > width)
width = widthCurr;
}
width = (width + 7) / 8;
if (width & 1)
width++;
window.tilemapLeft = (30 - width) / 2;
window.width = width;
2021-03-01 07:54:51 +01:00
tWindowId = AddWindow(&window);
PrintRecordsText(tWindowId, width);
2021-11-03 20:29:18 +01:00
CopyWindowToVram(tWindowId, COPYWIN_FULL);
2021-03-01 07:54:51 +01:00
tState++;
2019-08-01 19:01:50 +02:00
break;
case 1:
if (!IsDma3ManagerBusyWithBgCopy())
2021-03-01 07:54:51 +01:00
tState++;
2019-08-01 19:01:50 +02:00
break;
case 2:
2020-09-05 03:11:55 +02:00
if (JOY_NEW(A_BUTTON | B_BUTTON))
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
rbox_fill_rectangle(tWindowId);
2021-11-03 20:29:18 +01:00
CopyWindowToVram(tWindowId, COPYWIN_MAP);
2021-03-01 07:54:51 +01:00
tState++;
2019-08-01 19:01:50 +02:00
}
break;
case 3:
if (!IsDma3ManagerBusyWithBgCopy())
{
2021-03-01 07:54:51 +01:00
RemoveWindow(tWindowId);
2019-08-01 19:01:50 +02:00
DestroyTask(taskId);
ScriptContext_Enable();
2019-08-01 19:01:50 +02:00
}
break;
}
}
2021-03-01 07:54:51 +01:00
#undef tState
#undef tWindowId
static void PrintRecordsText(u8 windowId, s32 width)
2019-08-01 19:01:50 +02:00
{
s32 i, x, numWidth;
2021-03-01 07:54:51 +01:00
s32 recordNums[NUM_RECORD_TYPES];
recordNums[0] = gSaveBlock2Ptr->berryPick.berriesPicked;
recordNums[1] = gSaveBlock2Ptr->berryPick.bestScore;
recordNums[2] = gSaveBlock2Ptr->berryPick.berriesPickedInRow;
2019-08-01 19:01:50 +02:00
LoadUserWindowBorderGfx_(windowId, 0x21D, 0xD0);
DrawTextBorderOuter(windowId, 0x21D, 0xD);
FillWindowPixelBuffer(windowId, PIXEL_FILL(1));
AddTextPrinterParameterized(windowId, FONT_NORMAL, gText_BerryPickingRecords, GetStringCenterAlignXOffset(FONT_NORMAL, gText_BerryPickingRecords, width * 8), 1, TEXT_SKIP_DRAW, NULL);
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_RECORD_TYPES; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
ConvertIntToDecimalStringN(gStringVar1, recordNums[i], STR_CONV_MODE_LEFT_ALIGN, sRecordNumMaxDigits[i]);
2021-10-30 22:47:37 +02:00
numWidth = GetStringWidth(FONT_NORMAL, gStringVar1, -1);
AddTextPrinterParameterized(windowId, FONT_NORMAL, sRecordsTexts[i], 0, sRecordTextYCoords[i][0], TEXT_SKIP_DRAW, NULL);
2019-08-01 19:01:50 +02:00
x = (width * 8) - numWidth;
AddTextPrinterParameterized(windowId, FONT_NORMAL, gStringVar1, x, sRecordNumYCoords[i][0], TEXT_SKIP_DRAW, NULL);
2019-08-01 19:01:50 +02:00
}
PutWindowTilemap(windowId);
}
// Debug functions?
2021-03-01 07:54:51 +01:00
static const u16 sDebug_BerryResults[MAX_RFU_PLAYERS][4] =
{
{
[BERRY_BLUE] = MAX_BERRIES,
[BERRY_GREEN] = 0,
[BERRY_GOLD] = 90,
[BERRY_MISSED] = MAX_BERRIES
},
2021-03-01 07:54:51 +01:00
{
[BERRY_BLUE] = MAX_BERRIES,
[BERRY_GREEN] = MAX_BERRIES,
[BERRY_GOLD] = 70,
[BERRY_MISSED] = MAX_BERRIES
},
2021-03-01 07:54:51 +01:00
{
[BERRY_BLUE] = MAX_BERRIES,
[BERRY_GREEN] = 0,
[BERRY_GOLD] = MAX_BERRIES,
[BERRY_MISSED] = 0
},
2021-03-01 07:54:51 +01:00
{
[BERRY_BLUE] = MAX_BERRIES,
[BERRY_GREEN] = MAX_BERRIES,
[BERRY_GOLD] = 60,
[BERRY_MISSED] = 0
},
{
[BERRY_BLUE] = MAX_BERRIES,
2021-03-01 07:54:51 +01:00
[BERRY_GREEN] = MAX_BERRIES,
[BERRY_GOLD] = MAX_BERRIES,
[BERRY_MISSED] = 0
},
2019-08-01 19:01:50 +02:00
};
2019-12-10 19:48:20 +01:00
static const u8 sJPText_Vowels[] = _("あいうえおかき");
static const u8 sText_Letters[] = _("ABCDEFG");
static const u8 sText_Digits[] = _("0123456");
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
static const u8 *const sDebug_PlayerNames[] =
2019-08-01 19:01:50 +02:00
{
2019-12-10 19:48:20 +01:00
sJPText_Vowels,
sJPText_Vowels,
sJPText_Vowels,
sText_Letters,
sText_Digits
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static void Debug_UpdateNumPlayers(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sGame->numPlayers = GetLinkPlayerCount();
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void Debug_SetPlayerNamesAndResults(void)
2019-08-01 19:01:50 +02:00
{
u8 i, playerId;
2021-03-01 07:54:51 +01:00
for (playerId = sGame->numPlayers; playerId < ARRAY_COUNT(sDebug_PlayerNames); playerId++)
StringCopy(gLinkPlayers[playerId].name, sDebug_PlayerNames[playerId]);
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
sGame->numPlayers = MAX_RFU_PLAYERS;
for (i = 0; i < NUM_BERRY_TYPES; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
for (playerId = 0; playerId < sGame->numPlayers; playerId++)
sGame->berryResults[playerId][i] = sDebug_BerryResults[playerId][i];
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
struct ReadyToStartPacket
2019-08-01 19:01:50 +02:00
{
u8 id;
2021-03-01 07:54:51 +01:00
bool8 ALIGNED(4) ready;
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static void SendPacket_ReadyToStart(bool32 ready)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct ReadyToStartPacket packet;
packet.id = PACKET_READY_START;
packet.ready = ready;
2020-08-24 20:52:33 +02:00
Rfu_SendPacket(&packet);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static u32 RecvPacket_ReadyToStart(u32 playerId)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct ReadyToStartPacket *packet;
2019-08-01 19:01:50 +02:00
if ((gRecvCmds[0][0] & RFUCMD_MASK) != RFUCMD_SEND_PACKET)
2021-03-01 07:54:51 +01:00
return FALSE;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
packet = (void *)&gRecvCmds[playerId][1];
if (packet->id == PACKET_READY_START)
return packet->ready;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
return FALSE;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
struct GameStatePacket
2019-08-01 19:01:50 +02:00
{
u8 id;
2021-03-01 07:54:51 +01:00
u8 fallDist_Col0:4;
u8 fallDist_Col1:4;
u16 fallDist_Col2:4;
u16 fallDist_Col3:4;
u16 fallDist_Col4:4;
u16 fallDist_Col5:4;
u16 fallDist_Col6:4;
u16 fallDist_Col7:4;
u16 fallDist_Col8:4;
u16 fallDist_Col9:4;
u16 berryId_Col0:2;
u16 berryId_Col1:2;
u16 berryId_Col2:2;
u16 berryId_Col3:2;
u16 berryId_Col4:2;
u16 berryId_Col5:2;
u16 berryId_Col6:2;
u16 berryId_Col7:2;
u8 berryId_Col8:2;
u8 berryId_Col9:2;
u8 pickState_Player1:2;
u8 pickState_Player2:2;
u8 pickState_Player3:2;
u8 pickState_Player4:2;
u8 pickState_Player5:2;
bool8 ateBerry_Player1:1;
bool8 ateBerry_Player2:1;
bool8 ateBerry_Player3:1;
bool8 ateBerry_Player4:1;
bool8 ateBerry_Player5:1;
u8 numGraySquares:5;
bool8 allReadyToEnd:1;
bool8 berriesFalling:1;
bool8 missedBerry_Player1:1;
bool8 missedBerry_Player2:1;
bool8 missedBerry_Player3:1;
bool8 missedBerry_Player4:1;
bool8 missedBerry_Player5:1;
2019-08-01 19:01:50 +02:00
};
static void SendPacket_GameState(struct DodrioGame_Player *player,
struct DodrioGame_PlayerCommData *player1,
struct DodrioGame_PlayerCommData *player2,
struct DodrioGame_PlayerCommData *player3,
struct DodrioGame_PlayerCommData *player4,
struct DodrioGame_PlayerCommData *player5,
u8 numGraySquares,
bool32 berriesFalling,
2021-03-01 07:54:51 +01:00
bool32 allReadyToEnd)
{
struct GameStatePacket packet;
struct DodrioGame_Berries *berries = &player->berries;
packet.id = PACKET_GAME_STATE;
packet.fallDist_Col0 = berries->fallDist[0];
packet.fallDist_Col1 = berries->fallDist[1];
packet.fallDist_Col2 = berries->fallDist[2];
packet.fallDist_Col3 = berries->fallDist[3];
packet.fallDist_Col4 = berries->fallDist[4];
packet.fallDist_Col5 = berries->fallDist[5];
packet.fallDist_Col6 = berries->fallDist[6];
packet.fallDist_Col7 = berries->fallDist[7];
packet.fallDist_Col8 = berries->fallDist[8];
packet.fallDist_Col9 = berries->fallDist[9];
packet.berryId_Col0 = berries->ids[0];
packet.berryId_Col1 = berries->ids[1];
packet.berryId_Col2 = berries->ids[2];
packet.berryId_Col3 = berries->ids[3];
packet.berryId_Col4 = berries->ids[4];
packet.berryId_Col5 = berries->ids[5];
packet.berryId_Col6 = berries->ids[6];
packet.berryId_Col7 = berries->ids[7];
packet.berryId_Col8 = berries->ids[8];
packet.berryId_Col9 = berries->ids[9];
packet.pickState_Player1 = player1->pickState;
packet.pickState_Player2 = player2->pickState;
packet.pickState_Player3 = player3->pickState;
packet.pickState_Player4 = player4->pickState;
packet.pickState_Player5 = player5->pickState;
packet.ateBerry_Player1 = player1->ateBerry;
packet.ateBerry_Player2 = player2->ateBerry;
packet.ateBerry_Player3 = player3->ateBerry;
packet.ateBerry_Player4 = player4->ateBerry;
packet.ateBerry_Player5 = player5->ateBerry;
packet.missedBerry_Player1 = player1->missedBerry;
packet.missedBerry_Player2 = player2->missedBerry;
packet.missedBerry_Player3 = player3->missedBerry;
packet.missedBerry_Player4 = player4->missedBerry;
packet.missedBerry_Player5 = player5->missedBerry;
packet.numGraySquares = numGraySquares;
packet.berriesFalling = berriesFalling;
packet.allReadyToEnd = allReadyToEnd;
2020-08-24 20:52:33 +02:00
Rfu_SendPacket(&packet);
2019-08-01 19:01:50 +02:00
}
static bool32 RecvPacket_GameState(u32 playerId,
struct DodrioGame_Player *player,
struct DodrioGame_PlayerCommData *player1,
struct DodrioGame_PlayerCommData *player2,
struct DodrioGame_PlayerCommData *player3,
struct DodrioGame_PlayerCommData *player4,
struct DodrioGame_PlayerCommData *player5,
u8 *numGraySquares,
bool32 *berriesFalling,
2021-03-01 07:54:51 +01:00
bool32 *allReadyToEnd)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct GameStatePacket *packet;
struct DodrioGame_Berries *berries = &player->berries;
2019-08-01 19:01:50 +02:00
if ((gRecvCmds[0][0] & RFUCMD_MASK) != RFUCMD_SEND_PACKET)
2021-03-01 07:54:51 +01:00
return FALSE;
2019-08-01 19:01:50 +02:00
packet = (void *)&gRecvCmds[0][1];
2021-03-01 07:54:51 +01:00
if (packet->id == PACKET_GAME_STATE)
{
berries->fallDist[0] = packet->fallDist_Col0;
berries->fallDist[1] = packet->fallDist_Col1;
berries->fallDist[2] = packet->fallDist_Col2;
berries->fallDist[3] = packet->fallDist_Col3;
berries->fallDist[4] = packet->fallDist_Col4;
berries->fallDist[5] = packet->fallDist_Col5;
berries->fallDist[6] = packet->fallDist_Col6;
berries->fallDist[7] = packet->fallDist_Col7;
berries->fallDist[8] = packet->fallDist_Col8;
berries->fallDist[9] = packet->fallDist_Col9;
berries->fallDist[10] = packet->fallDist_Col0;
berries->ids[0] = packet->berryId_Col0;
berries->ids[1] = packet->berryId_Col1;
berries->ids[2] = packet->berryId_Col2;
berries->ids[3] = packet->berryId_Col3;
berries->ids[4] = packet->berryId_Col4;
berries->ids[5] = packet->berryId_Col5;
berries->ids[6] = packet->berryId_Col6;
berries->ids[7] = packet->berryId_Col7;
berries->ids[8] = packet->berryId_Col8;
berries->ids[9] = packet->berryId_Col9;
berries->ids[10] = packet->berryId_Col0;
player1->pickState = packet->pickState_Player1;
player1->ateBerry = packet->ateBerry_Player1;
player1->missedBerry = packet->missedBerry_Player1;
player2->pickState = packet->pickState_Player2;
player2->ateBerry = packet->ateBerry_Player2;
player2->missedBerry = packet->missedBerry_Player2;
player3->pickState = packet->pickState_Player3;
player3->ateBerry = packet->ateBerry_Player3;
player3->missedBerry = packet->missedBerry_Player3;
player4->pickState = packet->pickState_Player4;
player4->ateBerry = packet->ateBerry_Player4;
player4->missedBerry = packet->missedBerry_Player4;
player5->pickState = packet->pickState_Player5;
player5->ateBerry = packet->ateBerry_Player5;
player5->missedBerry = packet->missedBerry_Player5;
*numGraySquares = packet->numGraySquares;
*berriesFalling = packet->berriesFalling;
*allReadyToEnd = packet->allReadyToEnd;
return TRUE;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
return FALSE;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
struct PickStatePacket
2019-08-01 19:01:50 +02:00
{
u8 id;
2021-03-01 07:54:51 +01:00
u8 ALIGNED(4) pickState;
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static void SendPacket_PickState(u8 pickState)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct PickStatePacket packet;
packet.id = PACKET_PICK_STATE;
packet.pickState = pickState;
2020-08-24 20:52:33 +02:00
Rfu_SendPacket(&packet);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static bool32 RecvPacket_PickState(u32 playerId, u8 *pickState)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct PickStatePacket *packet;
2019-08-01 19:01:50 +02:00
if ((gRecvCmds[0][0] & RFUCMD_MASK) != RFUCMD_SEND_PACKET)
2021-03-01 07:54:51 +01:00
return FALSE;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
packet = (void *)&gRecvCmds[playerId][1];
if (packet->id == PACKET_PICK_STATE)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
*pickState = packet->pickState;
return TRUE;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
return FALSE;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
struct ReadyToEndPacket
2019-08-01 19:01:50 +02:00
{
u8 id;
2021-03-01 07:54:51 +01:00
bool32 ready;
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static void SendPacket_ReadyToEnd(bool32 ready)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct ReadyToEndPacket packet;
packet.id = PACKET_READY_END;
packet.ready = ready;
2020-08-24 20:52:33 +02:00
Rfu_SendPacket(&packet);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static bool32 RecvPacket_ReadyToEnd(u32 playerId)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct ReadyToEndPacket *packet;
2019-08-01 19:01:50 +02:00
if ((gRecvCmds[0][0] & RFUCMD_MASK) != RFUCMD_SEND_PACKET)
2021-03-01 07:54:51 +01:00
return FALSE;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
packet = (void *)&gRecvCmds[playerId][1];
if (packet->id == PACKET_READY_END)
return packet->ready;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
return FALSE;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static const struct BgTemplate sBgTemplates[] =
2019-08-01 19:01:50 +02:00
{
{
2021-03-01 07:54:51 +01:00
.bg = BG_INTERFACE,
2019-08-01 19:01:50 +02:00
.charBaseIndex = 0,
.mapBaseIndex = 30,
.screenSize = 0,
.paletteMode = 0,
.priority = 0,
.baseTile = 0
},
{
2021-03-01 07:54:51 +01:00
.bg = BG_TREE_LEFT,
2019-08-01 19:01:50 +02:00
.charBaseIndex = 2,
.mapBaseIndex = 12,
.screenSize = 1,
.paletteMode = 0,
.priority = 1,
.baseTile = 0
},
{
2021-03-01 07:54:51 +01:00
.bg = BG_TREE_RIGHT,
2019-08-01 19:01:50 +02:00
.charBaseIndex = 2,
.mapBaseIndex = 14,
.screenSize = 1,
.paletteMode = 0,
.priority = 1,
.baseTile = 0
},
{
2021-03-01 07:54:51 +01:00
.bg = BG_SCENERY,
2019-08-01 19:01:50 +02:00
.charBaseIndex = 3,
.mapBaseIndex = 31,
.screenSize = 0,
.paletteMode = 0,
.priority = 2,
.baseTile = 0
},
};
2021-03-01 07:54:51 +01:00
static const struct WindowTemplate sWindowTemplate_Dummy = DUMMY_WIN_TEMPLATE;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
static const struct WindowTemplate sWindowTemplates_Results[] =
2019-08-01 19:01:50 +02:00
{
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
.bg = BG_INTERFACE,
2019-08-02 00:41:55 +02:00
.tilemapLeft = 1,
.tilemapTop = 1,
.width = 28,
.height = 2,
.paletteNum = 13,
.baseBlock = 0x13,
},
{
2021-03-01 07:54:51 +01:00
.bg = BG_INTERFACE,
2019-08-02 00:41:55 +02:00
.tilemapLeft = 1,
.tilemapTop = 5,
.width = 28,
.height = 14,
.paletteNum = 13,
.baseBlock = 0x4B,
}
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static const struct WindowTemplate sWindowTemplate_Prize =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
.bg = BG_INTERFACE,
2019-08-01 19:01:50 +02:00
.tilemapLeft = 1,
.tilemapTop = 5,
.width = 28,
.height = 7,
.paletteNum = 13,
.baseBlock = 0x4B,
};
2021-03-01 07:54:51 +01:00
enum {
WIN_PLAY_AGAIN,
WIN_YES_NO,
};
static const struct WindowTemplate sWindowTemplates_PlayAgain[] =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
[WIN_PLAY_AGAIN] = {
.bg = BG_INTERFACE,
2019-08-02 00:41:55 +02:00
.tilemapLeft = 1,
.tilemapTop = 8,
.width = 19,
.height = 3,
.paletteNum = 13,
.baseBlock = 0x13,
},
2021-03-01 07:54:51 +01:00
[WIN_YES_NO] = {
.bg = BG_INTERFACE,
2019-08-02 00:41:55 +02:00
.tilemapLeft = 22,
.tilemapTop = 7,
.width = 6,
.height = 4,
.paletteNum = 13,
.baseBlock = 0x4C,
}
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static const struct WindowTemplate sWindowTemplate_DroppedOut =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
.bg = BG_INTERFACE,
2019-08-01 19:01:50 +02:00
.tilemapLeft = 4,
.tilemapTop = 6,
.width = 22,
.height = 5,
.paletteNum = 13,
.baseBlock = 0x13,
};
2021-03-01 07:54:51 +01:00
static const struct WindowTemplate sWindowTemplate_CommStandby =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
.bg = BG_INTERFACE,
2019-08-01 19:01:50 +02:00
.tilemapLeft = 5,
.tilemapTop = 8,
.width = 19,
.height = 3,
.paletteNum = 13,
.baseBlock = 0x13,
};
2021-03-01 07:54:51 +01:00
// Unused duplicate of sActiveColumnMap
static const u8 sActiveColumnMap_Duplicate[MAX_RFU_PLAYERS][MAX_RFU_PLAYERS][NUM_BERRY_COLUMNS] =
{
{
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
},
{
{0, 1, 2, 3, 4, 5, 6, 3, 8, 9, 0},
{0, 1, 2, 5, 6, 3, 4, 5, 8, 9, 0},
},
{
{0, 1, 2, 3, 4, 5, 6, 7, 2, 9, 0},
{0, 1, 4, 5, 6, 7, 2, 3, 4, 9, 0},
{0, 1, 6, 7, 2, 3, 4, 5, 6, 9, 0},
},
{
{0, 1, 2, 3, 4, 5, 6, 7, 8, 1, 0},
{0, 3, 4, 5, 6, 7, 8, 1, 2, 3, 0},
{0, 5, 6, 7, 8, 1, 2, 3, 4, 5, 0},
{0, 7, 8, 1, 2, 3, 4, 5, 6, 7, 0},
},
{
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0},
{2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2},
{4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4},
{6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6},
{8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8},
},
};
2021-03-01 07:54:51 +01:00
// Unused duplicate of sDodrioHeadToColumnMap
static const u8 sDodrioHeadToColumnMap_Duplicate[MAX_RFU_PLAYERS][MAX_RFU_PLAYERS][3] =
{
{
{4, 5, 6},
},
{
{3, 4, 5},
{5, 6, 3},
},
{
{4, 5, 6},
{6, 7, 2},
{2, 3, 4},
},
{
{3, 4, 5},
{5, 6, 7},
{7, 8, 1},
{1, 2, 3},
},
{
{4, 5, 6},
{6, 7, 8},
{8, 9, 0},
{0, 1, 2},
{2, 3, 4},
},
};
// Unused duplicate of sDodrioNeighborMap
static const u8 sDodrioNeighborMap_Duplicate[MAX_RFU_PLAYERS][MAX_RFU_PLAYERS][3] =
{
{
{1, 0, 1},
},
{
{1, 0, 1},
{0, 1, 0},
},
{
{2, 0, 1},
{0, 1, 2},
{1, 2, 0},
},
{
{3, 0, 1},
{0, 1, 2},
{1, 2, 3},
{2, 3, 0},
},
{
{4, 0, 1},
{0, 1, 2},
{1, 2, 3},
{2, 3, 4},
{3, 4, 0},
},
};
// Unused duplicate of sPlayerIdAtColumn
ALIGNED(4)
static const u8 sPlayerIdAtColumn_Duplicate[MAX_RFU_PLAYERS][NUM_BERRY_COLUMNS] =
{
{9, 9, 9, 9, 1, 1, 1, 9, 9, 9, 9},
{9, 9, 9, 0, 0, 1, 1, 0, 9, 9, 9},
{9, 9, 2, 2, 0, 0, 1, 1, 1, 9, 9},
{9, 3, 3, 0, 0, 1, 1, 2, 2, 3, 9},
{3, 3, 4, 4, 0, 0, 1, 1, 2, 2, 3},
};
// Unused duplicate of sUnsharedColumns
static const u8 sUnsharedColumns_Duplicate[MAX_RFU_PLAYERS][MAX_RFU_PLAYERS] =
{
{5},
{4, 6},
{3, 5, 7},
{2, 4, 6, 8},
{1, 3, 5, 6, 9},
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static const u16 sBg_Pal[] = INCBIN_U16("graphics/dodrio_berry_picking/bg.gbapal",
"graphics/dodrio_berry_picking/tree_border.gbapal");
static const u16 sDodrioNormal_Pal[] = INCBIN_U16("graphics/dodrio_berry_picking/dodrio.gbapal");
static const u16 sDodrioShiny_Pal[] = INCBIN_U16("graphics/dodrio_berry_picking/shiny.gbapal");
static const u16 sStatus_Pal[] = INCBIN_U16("graphics/dodrio_berry_picking/status.gbapal");
static const u16 sBerries_Pal[] = INCBIN_U16("graphics/dodrio_berry_picking/berries.gbapal");
static const u32 sBerries_Gfx[] = INCBIN_U32("graphics/dodrio_berry_picking/berries.4bpp.lz");
static const u16 sCloud_Pal[] = INCBIN_U16("graphics/dodrio_berry_picking/cloud.gbapal");
static const u32 sBg_Gfx[] = INCBIN_U32("graphics/dodrio_berry_picking/bg.4bpp.lz");
static const u32 sTreeBorder_Gfx[] = INCBIN_U32("graphics/dodrio_berry_picking/tree_border.4bpp.lz");
static const u32 sStatus_Gfx[] = INCBIN_U32("graphics/dodrio_berry_picking/status.4bpp.lz");
static const u32 sCloud_Gfx[] = INCBIN_U32("graphics/dodrio_berry_picking/cloud.4bpp.lz");
static const u32 sDodrio_Gfx[] = INCBIN_U32("graphics/dodrio_berry_picking/dodrio.4bpp.lz");
static const u32 sBg_Tilemap[] = INCBIN_U32("graphics/dodrio_berry_picking/bg.bin.lz");
static const u32 sTreeBorderRight_Tilemap[] = INCBIN_U32("graphics/dodrio_berry_picking/tree_border_right.bin.lz");
static const u32 sTreeBorderLeft_Tilemap[] = INCBIN_U32("graphics/dodrio_berry_picking/tree_border_left.bin.lz");
static const struct OamData sOamData_Dodrio =
2019-08-01 19:01:50 +02:00
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
2022-07-30 03:27:39 +02:00
.mosaic = FALSE,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(64x64),
2019-08-01 19:01:50 +02:00
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(64x64),
2019-08-01 19:01:50 +02:00
.tileNum = 0,
.priority = 2,
.paletteNum = 0,
.affineParam = 0
};
2021-03-01 07:54:51 +01:00
// Used by the status bar and the results screen berry icons
static const struct OamData sOamData_16x16_Priority0 =
2019-08-01 19:01:50 +02:00
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
2022-07-30 03:27:39 +02:00
.mosaic = FALSE,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(16x16),
2019-08-01 19:01:50 +02:00
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(16x16),
2019-08-01 19:01:50 +02:00
.tileNum = 0,
.priority = 0,
.paletteNum = 0,
.affineParam = 0
};
2021-03-01 07:54:51 +01:00
static const struct OamData sOamData_Berry =
2019-08-01 19:01:50 +02:00
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
2022-07-30 03:27:39 +02:00
.mosaic = FALSE,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(16x16),
2019-08-01 19:01:50 +02:00
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(16x16),
2019-08-01 19:01:50 +02:00
.tileNum = 0,
.priority = 2,
.paletteNum = 0,
.affineParam = 0
};
2021-03-01 07:54:51 +01:00
static const struct OamData sOamData_Cloud =
2019-08-01 19:01:50 +02:00
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
2022-07-30 03:27:39 +02:00
.mosaic = FALSE,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(64x32),
2019-08-01 19:01:50 +02:00
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(64x32),
2019-08-01 19:01:50 +02:00
.tileNum = 0,
.priority = 3,
.paletteNum = 0,
.affineParam = 0
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Dodrio_Normal[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(0, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Dodrio_PickRight[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(64, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Dodrio_PickMiddle[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(128, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Dodrio_PickLeft[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(192, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Dodrio_Down[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(256, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd *const sAnims_Dodrio[] =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
[PICK_NONE] = sAnim_Dodrio_Normal,
[PICK_RIGHT] = sAnim_Dodrio_PickRight,
[PICK_MIDDLE] = sAnim_Dodrio_PickMiddle,
[PICK_LEFT] = sAnim_Dodrio_PickLeft,
[PICK_DISABLED] = sAnim_Dodrio_Down,
// There is an unused 6th frame of Dodrio's graphic
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnims_StatusBar_Yellow[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(0, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnims_StatusBar_Gray[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(4, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnims_StatusBar_Red[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(8, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd *const sAnims_StatusBar[] =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
[STATUS_YELLOW] = sAnims_StatusBar_Yellow,
[STATUS_GRAY] = sAnims_StatusBar_Gray,
[STATUS_RED] = sAnims_StatusBar_Red
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Berry_Blue[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(0, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Berry_Green[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(4, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Berry_Gold[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(8, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Berry_BlueSquished[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(12, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Berry_GreenSquished[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(16, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Berry_GoldSquished[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(20, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Berry_Eaten[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(24, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Berry_Empty1[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(28, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Berry_Empty2[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(32, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd *const sAnims_Berry[] =
{
[BERRY_BLUE] = sAnim_Berry_Blue,
[BERRY_GREEN] = sAnim_Berry_Green,
[BERRY_GOLD] = sAnim_Berry_Gold,
[BERRY_BLUE + BERRY_MISSED] = sAnim_Berry_BlueSquished,
[BERRY_GREEN + BERRY_MISSED] = sAnim_Berry_GreenSquished,
[BERRY_GOLD + BERRY_MISSED] = sAnim_Berry_GoldSquished,
[ANIM_EATEN] = sAnim_Berry_Eaten,
sAnim_Berry_Empty1,
sAnim_Berry_Empty2
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd sAnim_Cloud[] =
2019-08-01 19:01:50 +02:00
{
ANIMCMD_FRAME(0, 20),
ANIMCMD_JUMP(0)
};
2021-03-01 07:54:51 +01:00
static const union AnimCmd *const sAnims_Cloud[] =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sAnim_Cloud
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static void LoadDodrioGfx(void)
2019-08-01 19:01:50 +02:00
{
void *ptr = AllocZeroed(0x3000);
2021-03-01 07:54:51 +01:00
struct SpritePalette normal = {sDodrioNormal_Pal, PALTAG_DODRIO_NORMAL};
struct SpritePalette shiny = {sDodrioShiny_Pal, PALTAG_DODRIO_SHINY};
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
LZ77UnCompWram(sDodrio_Gfx, ptr);
if (ptr)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct SpriteSheet sheet = {ptr, 0x3000, GFXTAG_DODRIO};
2019-08-01 19:01:50 +02:00
LoadSpriteSheet(&sheet);
Free(ptr);
}
2021-03-01 07:54:51 +01:00
LoadSpritePalette(&normal);
LoadSpritePalette(&shiny);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void CreateDodrioSprite(struct DodrioGame_MonInfo * monInfo, u8 playerId, u8 id, u8 numPlayers)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct SpriteTemplate template =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
.tileTag = GFXTAG_DODRIO,
.paletteTag = monInfo->isShiny, // PALTAG_DODRIO_NORMAL / PALTAG_DODRIO_SHINY
.oam = &sOamData_Dodrio,
.anims = sAnims_Dodrio,
2019-08-01 19:01:50 +02:00
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
2021-03-01 07:54:51 +01:00
.callback = SpriteCB_Dodrio,
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
sDodrioSpriteIds[id] = AllocZeroed(4);
*sDodrioSpriteIds[id] = CreateSprite(&template, GetDodrioXPos(playerId, numPlayers), 136, 3);
SetDodrioInvisibility(TRUE, id);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
#define sState data[0]
#define sTimer data[1]
#define sUnused1 data[2]
#define sUnused2 data[3]
#define sUnused3 data[4]
static void SpriteCB_Dodrio(struct Sprite *sprite)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
switch (sprite->sState)
2019-08-01 19:01:50 +02:00
{
case 0:
break;
case 1:
2021-03-01 07:54:51 +01:00
DoDodrioMissedAnim(sprite);
2019-08-01 19:01:50 +02:00
break;
case 2:
2021-03-01 07:54:51 +01:00
DoDodrioIntroAnim(sprite);
2019-08-01 19:01:50 +02:00
break;
}
}
2021-03-01 07:54:51 +01:00
static void StartDodrioMissedAnim(u8 unused)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct Sprite *sprite = &gSprites[*sDodrioSpriteIds[GetMultiplayerId()]];
sprite->sState = 1;
sprite->sTimer = 0;
sprite->sUnused1 = 0;
sprite->sUnused2 = 0;
sprite->sUnused3 = 0;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void StartDodrioIntroAnim(u8 unused)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct Sprite *sprite = &gSprites[*sDodrioSpriteIds[GetMultiplayerId()]];
sprite->sState = 2;
sprite->sTimer = 0;
sprite->sUnused1 = 0;
sprite->sUnused2 = 0;
sprite->sUnused3 = 0;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
// Do animation where Dodrio shakes horizontally after reaching for a berry and missing
static u32 DoDodrioMissedAnim(struct Sprite *sprite)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
s8 x;
u8 state = (++sprite->sTimer / 2) % 4;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
if (sprite->sTimer >= 3)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
switch (state)
2019-08-01 19:01:50 +02:00
{
default:
2021-03-01 07:54:51 +01:00
x = 1;
2019-08-01 19:01:50 +02:00
break;
case 1:
case 2:
2021-03-01 07:54:51 +01:00
x = -1;
2019-08-01 19:01:50 +02:00
break;
}
2021-07-07 15:11:52 +02:00
sprite->x += x;
2021-03-01 07:54:51 +01:00
if (++sprite->sTimer >= 40)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sprite->sState = 0;
2021-07-07 15:11:52 +02:00
sprite->x = GetDodrioXPos(0, GetNumPlayers());
2019-08-01 19:01:50 +02:00
}
}
return 0;
}
2021-03-01 07:54:51 +01:00
// Does the intro animation where the player's Dodrio
// cycles through extending each head twice
#define FRAMES_PER_STATE 13
#define NUM_INTRO_PICK_STATES PICK_DISABLED // Cycle through 'Normal' and each head, but exclude the Disabled state
static u32 DoDodrioIntroAnim(struct Sprite *sprite)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 pickState = (++sprite->sTimer / FRAMES_PER_STATE) % NUM_INTRO_PICK_STATES;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
// Play a sound effect at the start of each head extension
if (sprite->sTimer % FRAMES_PER_STATE == 0 && pickState != PICK_NONE)
2020-08-21 00:02:00 +02:00
PlaySE(SE_M_CHARM);
2021-03-01 07:54:51 +01:00
if (sprite->sTimer >= FRAMES_PER_STATE * NUM_INTRO_PICK_STATES * 2)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
// End animation
sprite->sState = 0;
pickState = PICK_NONE;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
SetDodrioAnim(GetMultiplayerId(), pickState);
2019-08-01 19:01:50 +02:00
return 0;
}
2021-03-01 07:54:51 +01:00
#undef sState
#undef sTimer
#undef sUnused1
#undef sUnused2
#undef sUnused3
static void FreeDodrioSprites(u8 numPlayers)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < numPlayers; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct Sprite *sprite = &gSprites[*sDodrioSpriteIds[i]];
if (sprite)
2019-08-01 19:01:50 +02:00
DestroySpriteAndFreeResources(sprite);
2021-03-01 07:54:51 +01:00
#ifdef BUGFIX
FREE_AND_SET_NULL(sDodrioSpriteIds[i]); // Memory should be freed here but is not.
#endif
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
static void SetDodrioInvisibility(bool8 invisible, u8 id)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
gSprites[*sDodrioSpriteIds[id]].invisible = invisible;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void SetAllDodrioInvisibility(bool8 invisible, u8 count)
2019-08-01 19:01:50 +02:00
{
u8 i;
for (i = 0; i < count; i++)
2021-03-01 07:54:51 +01:00
SetDodrioInvisibility(invisible, i);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void SetDodrioAnim(u8 id, u8 pickState)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
StartSpriteAnim(&gSprites[*sDodrioSpriteIds[id]], pickState);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void SpriteCB_Status(struct Sprite *sprite)
2019-08-01 19:01:50 +02:00
{
}
2021-03-01 07:54:51 +01:00
static void InitStatusBarPos(void)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_STATUS_SQUARES; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct Sprite *sprite = &gSprites[sStatusBar->spriteIds[i]];
2021-07-07 15:11:52 +02:00
sprite->x = (i * 16) + 48;
sprite->y = -8 - (i * 8);
2021-03-01 07:54:51 +01:00
sStatusBar->entered[i] = FALSE;
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
static void CreateStatusBarSprites(void)
2019-08-01 19:01:50 +02:00
{
u8 i;
void *ptr = AllocZeroed(0x180);
2021-03-01 07:54:51 +01:00
struct SpritePalette pal = {sStatus_Pal, PALTAG_STATUS};
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
LZ77UnCompWram(sStatus_Gfx, ptr);
2019-08-01 19:01:50 +02:00
// This check should be one line up.
2021-03-01 07:54:51 +01:00
if (ptr)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct SpriteSheet sheet = {ptr, 0x180, GFXTAG_STATUS};
struct SpriteTemplate template =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
.tileTag = GFXTAG_STATUS,
.paletteTag = PALTAG_STATUS,
.oam = &sOamData_16x16_Priority0,
.anims = sAnims_StatusBar,
2019-08-01 19:01:50 +02:00
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
2021-03-01 07:54:51 +01:00
.callback = SpriteCB_Status,
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
sStatusBar = AllocZeroed(sizeof(*sStatusBar));
LoadSpriteSheet(&sheet);
LoadSpritePalette(&pal);
for (i = 0; i < NUM_STATUS_SQUARES; i++)
sStatusBar->spriteIds[i] = CreateSprite(&template, (i * 16) + 48, -8 - (i * 8), 0);
2019-08-01 19:01:50 +02:00
}
Free(ptr);
}
2021-03-01 07:54:51 +01:00
static void FreeStatusBar(void)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_STATUS_SQUARES; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct Sprite *sprite = &gSprites[sStatusBar->spriteIds[i]];
if (sprite)
2019-08-01 19:01:50 +02:00
DestroySpriteAndFreeResources(sprite);
}
2021-03-01 07:54:51 +01:00
FREE_AND_SET_NULL(sStatusBar);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
// Progress an animation where each square of the
// status bar drops down into view, bounces up,
// then settles into position.
// Returns TRUE if the animation is complete
static bool32 DoStatusBarIntro(void)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
bool32 animActive = FALSE;
for (i = 0; i < NUM_STATUS_SQUARES; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct Sprite *sprite = &gSprites[sStatusBar->spriteIds[i]];
sStatusBar->yChange[i] = 2;
2021-07-07 15:11:52 +02:00
if (sStatusBar->entered[i] && sprite->y == 8)
2019-08-01 19:01:50 +02:00
continue;
2021-03-01 07:54:51 +01:00
animActive = TRUE;
2021-07-07 15:11:52 +02:00
if (sprite->y == 8)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
if (sStatusBar->entered[i])
2019-08-01 19:01:50 +02:00
continue;
2021-03-01 07:54:51 +01:00
// Square has entered screen, play click
// sound and reverse direction
sStatusBar->entered[i] = TRUE;
sStatusBar->yChange[i] = -16;
2020-08-21 00:02:00 +02:00
PlaySE(SE_CLICK);
2019-08-01 19:01:50 +02:00
}
2021-07-07 15:11:52 +02:00
sprite->y += sStatusBar->yChange[i];
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
if (animActive)
2019-08-01 19:01:50 +02:00
return FALSE;
else
return TRUE;
}
2021-03-01 07:54:51 +01:00
// The status bar at the top changes color depending on the game performance.
// The squares start out yellow. For every berry missed, a square is colored gray.
// If there are 4 or fewer yellow squares left they also flash red
static void UpdateStatusBarAnim(u8 numEmpty)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
if (numEmpty > NUM_STATUS_SQUARES)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
// All squares gray
for (i = 0; i < NUM_STATUS_SQUARES; i++)
StartSpriteAnim(&gSprites[sStatusBar->spriteIds[i]], STATUS_GRAY);
2019-08-01 19:01:50 +02:00
}
else
{
2021-03-01 07:54:51 +01:00
// At least 1 square is yellow
for (i = 0; i < NUM_STATUS_SQUARES - numEmpty; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
if (numEmpty > 6)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
// Flash the yellow squares red
// The flash cycles faster the fewer yellow squares remain
sStatusBar->flashTimer += numEmpty - 6;
if (sStatusBar->flashTimer > 30)
sStatusBar->flashTimer = 0;
else if (sStatusBar->flashTimer > 10)
StartSpriteAnim(&gSprites[sStatusBar->spriteIds[i]], STATUS_RED);
2019-08-01 19:01:50 +02:00
else
2021-03-01 07:54:51 +01:00
StartSpriteAnim(&gSprites[sStatusBar->spriteIds[i]], STATUS_YELLOW);
2019-08-01 19:01:50 +02:00
}
else
{
2021-03-01 07:54:51 +01:00
// Set yellow squares, no flash
StartSpriteAnim(&gSprites[sStatusBar->spriteIds[i]], STATUS_YELLOW);
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
// Set remaining squares gray
for (; i < NUM_STATUS_SQUARES; i++)
StartSpriteAnim(&gSprites[sStatusBar->spriteIds[i]], STATUS_GRAY);
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
static void SetStatusBarInvisibility(bool8 invisible)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_STATUS_SQUARES; i++)
gSprites[sStatusBar->spriteIds[i]].invisible = invisible;
}
static const u8 sUnusedSounds[] = {
SE_M_CHARM,
SE_NOTE_C,
SE_NOTE_D,
SE_NOTE_E,
SE_NOTE_F,
SE_NOTE_G,
SE_NOTE_A,
SE_NOTE_B,
SE_NOTE_C_HIGH,
2021-03-01 07:54:51 +01:00
SE_RG_CARD_OPEN
};
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
static void LoadBerryGfx(void)
2019-08-01 19:01:50 +02:00
{
void *ptr = AllocZeroed(0x480);
2021-03-01 07:54:51 +01:00
struct SpritePalette pal = {sBerries_Pal, PALTAG_BERRIES};
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
LZ77UnCompWram(sBerries_Gfx, ptr);
if (ptr)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct SpriteSheet sheet = {ptr, 0x480, GFXTAG_BERRIES};
LoadSpriteSheet(&sheet);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
LoadSpritePalette(&pal);
2019-08-01 19:01:50 +02:00
Free(ptr);
}
2021-03-01 07:54:51 +01:00
static const s16 sBerryIconXCoords[] = {88, 128, 168, 208};
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
static void CreateBerrySprites(void)
2019-08-01 19:01:50 +02:00
{
u8 i;
s16 x;
2021-03-01 07:54:51 +01:00
struct SpriteTemplate berry =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
.tileTag = GFXTAG_BERRIES,
.paletteTag = PALTAG_BERRIES,
.oam = &sOamData_Berry,
.anims = sAnims_Berry,
2019-08-01 19:01:50 +02:00
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
2021-03-01 07:54:51 +01:00
struct SpriteTemplate berryIcon =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
.tileTag = GFXTAG_BERRIES,
.paletteTag = PALTAG_BERRIES,
.oam = &sOamData_16x16_Priority0,
.anims = sAnims_Berry,
2019-08-01 19:01:50 +02:00
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
2021-03-01 07:54:51 +01:00
// Create berry sprites that fall during gameplay
for (i = 0; i < NUM_BERRY_COLUMNS; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sBerrySpriteIds[i] = AllocZeroed(4);
2019-08-01 19:01:50 +02:00
x = i * 16;
2021-03-01 07:54:51 +01:00
*sBerrySpriteIds[i] = CreateSprite(&berry, x + (i * 8), 8, 1);
SetBerryInvisibility(i, TRUE);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
// Create berry icon sprites for results screen
for (i = 0; i < NUM_BERRY_TYPES; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sBerryIconSpriteIds[i] = AllocZeroed(4);
if (i == BERRY_MISSED)
*sBerryIconSpriteIds[i] = CreateSprite(&berryIcon, sBerryIconXCoords[i], 49, 0);
2019-08-01 19:01:50 +02:00
else
2021-03-01 07:54:51 +01:00
*sBerryIconSpriteIds[i] = CreateSprite(&berryIcon, sBerryIconXCoords[i], 52, 0);
StartSpriteAnim(&gSprites[*sBerryIconSpriteIds[i]], i);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
SetBerryIconsInvisibility(TRUE);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void FreeBerrySprites(void)
2019-08-01 19:01:50 +02:00
{
struct Sprite *sprite;
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_BERRY_COLUMNS; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sprite = &gSprites[*sBerrySpriteIds[i]];
if (sprite)
2019-08-01 19:01:50 +02:00
DestroySprite(sprite);
2021-03-01 07:54:51 +01:00
FREE_AND_SET_NULL(sBerrySpriteIds[i]);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_BERRY_TYPES; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sprite = &gSprites[*sBerryIconSpriteIds[i]];
if (sprite)
2019-08-01 19:01:50 +02:00
DestroySprite(sprite);
2021-03-01 07:54:51 +01:00
FREE_AND_SET_NULL(sBerryIconSpriteIds[i]);
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
static void SetBerryInvisibility(u8 id, bool8 invisible)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
gSprites[*sBerrySpriteIds[id]].invisible = invisible;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void SetBerryIconsInvisibility(bool8 invisible)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_BERRY_TYPES; i++)
gSprites[*sBerryIconSpriteIds[i]].invisible = invisible;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void SetBerryYPos(u8 id, u8 y)
2019-08-01 19:01:50 +02:00
{
2021-07-07 15:11:52 +02:00
gSprites[*sBerrySpriteIds[id]].y = y * 8;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void SetBerryAnim(u16 id, u8 animNum)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
StartSpriteAnim(&gSprites[*sBerrySpriteIds[id]], animNum);
2019-08-01 19:01:50 +02:00
}
// Unused
2021-03-01 07:54:51 +01:00
static void UnusedSetSpritePos(u8 spriteId)
2019-08-01 19:01:50 +02:00
{
2021-07-07 15:11:52 +02:00
gSprites[spriteId].x = 20 * spriteId + 50;
gSprites[spriteId].y = 50;
2019-08-01 19:01:50 +02:00
}
// Gamefreak made a mistake there and goes out of bounds for the data array as it holds 8 elements
// in turn overwriting sprite's subpriority and subsprites fields.
2021-03-01 07:54:51 +01:00
#ifdef UBFIX
#define sFrozen data[1]
2019-08-01 19:01:50 +02:00
#else
2021-03-01 07:54:51 +01:00
#define sFrozen data[10]
#endif // UBFIX
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
static void SpriteCB_Cloud(struct Sprite *sprite)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
static const u8 moveDelays[] = {30, 20};
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
if (sprite->sFrozen != TRUE)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_CLOUDS; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
if (++sCloudSpriteIds[i][1] > moveDelays[i])
2019-08-01 19:01:50 +02:00
{
2021-07-07 15:11:52 +02:00
sprite->x--;
2021-03-01 07:54:51 +01:00
sCloudSpriteIds[i][1] = 0;
2019-08-01 19:01:50 +02:00
}
}
}
}
static const s16 sCloudStartCoords[NUM_CLOUDS][2] =
2021-03-01 07:54:51 +01:00
{
{230, 55},
2021-03-01 07:54:51 +01:00
{ 30, 74}
};
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
static void CreateCloudSprites(void)
2019-08-01 19:01:50 +02:00
{
u8 i;
void *ptr = AllocZeroed(0x400);
2021-03-01 07:54:51 +01:00
struct SpritePalette pal = {sCloud_Pal, PALTAG_CLOUD};
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
LZ77UnCompWram(sCloud_Gfx, ptr);
if (ptr)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct SpriteSheet sheet = {ptr, 0x400, GFXTAG_CLOUD};
struct SpriteTemplate template =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
.tileTag = GFXTAG_CLOUD,
.paletteTag = PALTAG_CLOUD,
.oam = &sOamData_Cloud,
.anims = sAnims_Cloud,
2019-08-01 19:01:50 +02:00
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
2021-03-01 07:54:51 +01:00
.callback = SpriteCB_Cloud,
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
LoadSpriteSheet(&sheet);
LoadSpritePalette(&pal);
for (i = 0; i < NUM_CLOUDS; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sCloudSpriteIds[i] = AllocZeroed(4);
*sCloudSpriteIds[i] = CreateSprite(&template, sCloudStartCoords[i][0], sCloudStartCoords[i][1], 4);
2019-08-01 19:01:50 +02:00
}
}
Free(ptr);
}
2021-03-01 07:54:51 +01:00
static void ResetCloudPos(void)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_CLOUDS; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct Sprite *sprite = &gSprites[*sCloudSpriteIds[i]];
sprite->sFrozen = TRUE;
2021-07-07 15:11:52 +02:00
sprite->x = sCloudStartCoords[i][0];
sprite->y = sCloudStartCoords[i][1];
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
static void StartCloudMovement(void)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_CLOUDS; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct Sprite *sprite = &gSprites[*sCloudSpriteIds[i]];
sprite->sFrozen = FALSE;
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
static void FreeCloudSprites(void)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_CLOUDS; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
struct Sprite *sprite = &gSprites[*sCloudSpriteIds[i]];
2019-08-01 19:01:50 +02:00
if (sprite)
DestroySprite(sprite);
2021-03-01 07:54:51 +01:00
FREE_AND_SET_NULL(sCloudSpriteIds[i]);
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
static void SetCloudInvisibility(bool8 invisible)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_CLOUDS; i++)
gSprites[*sCloudSpriteIds[i]].invisible = invisible;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
#undef sFrozen
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
static s16 GetDodrioXPos(u8 playerId, u8 numPlayers)
2019-08-01 19:01:50 +02:00
{
s16 x = 0;
2021-03-01 07:54:51 +01:00
switch (numPlayers)
2019-08-01 19:01:50 +02:00
{
case 1:
x = 15;
break;
case 2:
2021-03-01 07:54:51 +01:00
switch (playerId)
2019-08-01 19:01:50 +02:00
{
case 0: x = 12; break;
case 1: x = 18; break;
}
break;
case 3:
2021-03-01 07:54:51 +01:00
switch (playerId)
2019-08-01 19:01:50 +02:00
{
case 0: x = 15; break;
case 1: x = 21; break;
case 2: x = 9; break;
}
break;
case 4:
2021-03-01 07:54:51 +01:00
switch (playerId)
2019-08-01 19:01:50 +02:00
{
case 0: x = 12; break;
case 1: x = 18; break;
case 2: x = 24; break;
case 3: x = 6; break;
}
break;
case 5:
2021-03-01 07:54:51 +01:00
switch (playerId)
2019-08-01 19:01:50 +02:00
{
case 0: x = 15; break;
case 1: x = 21; break;
case 2: x = 27; break;
case 3: x = 3; break;
case 4: x = 9; break;
}
break;
}
return x * 8;
}
2021-03-01 07:54:51 +01:00
static void ResetBerryAndStatusBarSprites(void)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < NUM_BERRY_COLUMNS; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
SetBerryInvisibility(i, TRUE);
SetBerryYPos(i, 1);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
SetStatusBarInvisibility(FALSE);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void LoadWindowFrameGfx(u8 frameId)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
LoadBgTiles(BG_INTERFACE, GetWindowFrameTilesPal(frameId)->tiles, 0x120, 1);
2019-08-01 19:01:50 +02:00
LoadPalette(GetWindowFrameTilesPal(frameId)->pal, 0xA0, 0x20);
}
2021-03-01 07:54:51 +01:00
static void LoadUserWindowFrameGfx(void)
2019-08-01 19:01:50 +02:00
{
LoadUserWindowBorderGfx_(0, 0xA, 0xB0);
}
2021-03-01 07:54:51 +01:00
static void ResetGfxState(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sGfx->finished = FALSE;
sGfx->state = 0;
sGfx->loadState = 0;
sGfx->cursorSelection = 0;
sGfx->playAgainState = PLAY_AGAIN_NONE;
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void DrawYesNoMessageWindow(const struct WindowTemplate *template)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 pal = 10;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
FillBgTilemapBufferRect(BG_INTERFACE, 1, template->tilemapLeft - 1, template->tilemapTop - 1, 1, 1, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 2, template->tilemapLeft, template->tilemapTop - 1, template->width, 1, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 3, template->tilemapLeft + template->width, template->tilemapTop - 1, 1, 1, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 4, template->tilemapLeft - 1, template->tilemapTop, 1, template->height, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 6, template->tilemapLeft + template->width, template->tilemapTop, 1, template->height, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 7, template->tilemapLeft - 1, template->tilemapTop + template->height, 1, 1, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 8, template->tilemapLeft, template->tilemapTop + template->height, template->width, 1, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 9, template->tilemapLeft + template->width, template->tilemapTop + template->height, 1, 1, pal);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void DrawMessageWindow(const struct WindowTemplate *template)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 pal = 11;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
FillBgTilemapBufferRect(BG_INTERFACE, 10, template->tilemapLeft - 1, template->tilemapTop - 1, 1, 1, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 11, template->tilemapLeft, template->tilemapTop - 1, template->width, 1, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 12, template->tilemapLeft + template->width, template->tilemapTop - 1, 1, 1, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 13, template->tilemapLeft - 1, template->tilemapTop, 1, template->height, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 15, template->tilemapLeft + template->width, template->tilemapTop, 1, template->height, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 16, template->tilemapLeft - 1, template->tilemapTop + template->height, 1, 1, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 17, template->tilemapLeft, template->tilemapTop + template->height, template->width, 1, pal);
FillBgTilemapBufferRect(BG_INTERFACE, 18, template->tilemapLeft + template->width, template->tilemapTop + template->height, 1, 1, pal);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void InitGameGfx(struct DodrioGame_Gfx *ptr)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sGfx = ptr;
sGfx->finished = FALSE;
sGfx->state = 0;
sGfx->loadState = 0;
sGfx->cursorSelection = 0;
sGfx->playAgainState = PLAY_AGAIN_NONE;
sGfx->taskId = CreateTask(Task_TryRunGfxFunc, 3);
SetGfxFunc(LoadGfx);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
// Unused
static void FreeAllWindowBuffers_(void)
2019-08-01 19:01:50 +02:00
{
FreeAllWindowBuffers();
}
// Data used by functions below.
struct WinCoords
{
u8 left;
u8 top;
};
2021-03-01 07:54:51 +01:00
enum {
2021-04-10 04:39:34 +02:00
COLORID_GRAY,
2021-03-01 07:54:51 +01:00
COLORID_RED,
COLORID_BLUE,
COLORID_GREEN, // Unused
};
2019-12-10 19:48:20 +01:00
static const u8 sTextColorTable[][3] =
2019-08-01 19:01:50 +02:00
{
2021-04-10 04:39:34 +02:00
[COLORID_GRAY] = {TEXT_COLOR_WHITE, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY},
2021-03-01 07:54:51 +01:00
[COLORID_RED] = {TEXT_COLOR_WHITE, TEXT_COLOR_RED, TEXT_COLOR_LIGHT_RED},
[COLORID_BLUE] = {TEXT_COLOR_WHITE, TEXT_COLOR_BLUE, TEXT_COLOR_LIGHT_BLUE},
[COLORID_GREEN] = {TEXT_COLOR_WHITE, TEXT_COLOR_GREEN, TEXT_COLOR_LIGHT_GREEN},
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static const struct WinCoords sNameWindowCoords_1Player[] = {{12, 6}};
static const struct WinCoords sNameWindowCoords_2Players[] = {{9, 10}, {15, 6}};
static const struct WinCoords sNameWindowCoords_3Players[] = {{12, 6}, {18, 10}, {6, 10}};
static const struct WinCoords sNameWindowCoords_4Players[] = {{9, 10}, {15, 6}, {21, 10}, {3, 6}};
static const struct WinCoords sNameWindowCoords_5Players[] = {{12, 6}, {18, 10}, {23, 6}, {1, 6}, {6, 10}};
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
static const struct WinCoords *const sNameWindowCoords[MAX_RFU_PLAYERS] =
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
sNameWindowCoords_1Player,
sNameWindowCoords_2Players,
sNameWindowCoords_3Players,
sNameWindowCoords_4Players,
sNameWindowCoords_5Players,
2019-08-01 19:01:50 +02:00
};
2021-03-01 07:54:51 +01:00
static const u8 *const sRankingTexts[MAX_RFU_PLAYERS] =
2019-08-02 00:41:55 +02:00
{
gText_1Colon,
gText_2Colon,
gText_3Colon,
gText_4Colon,
gText_5Colon,
};
2021-03-01 07:54:51 +01:00
static const u16 sResultsXCoords[] = {92, 132, 172, 212};
static const u16 sResultsYCoords[] = {33, 49, 65, 81, 97};
static const u16 sRankingYCoords[] = {17, 33, 49, 65, 81};
2019-08-02 00:41:55 +02:00
struct
{
u8 id;
void (*func)(void);
2022-05-17 19:51:54 +02:00
} static const sGfxFuncs[] =
2021-03-01 07:54:51 +01:00
{
{GFXFUNC_LOAD, LoadGfx}, // Element not used, LoadGfx is passed directly to SetGfxFunc
{GFXFUNC_SHOW_NAMES, ShowNames},
{GFXFUNC_SHOW_RESULTS, ShowResults},
{GFXFUNC_MSG_PLAY_AGAIN, Msg_WantToPlayAgain},
{GFXFUNC_MSG_SAVING, Msg_SavingDontTurnOff},
{GFXFUNC_MSG_COMM_STANDBY, Msg_CommunicationStandby},
{GFXFUNC_ERASE_MSG, EraseMessage},
{GFXFUNC_MSG_PLAYER_DROPPED, Msg_SomeoneDroppedOut},
{GFXFUNC_STOP, StopGfxFuncs},
{GFXFUNC_IDLE, GfxIdle},
2019-08-02 00:41:55 +02:00
};
2021-03-01 07:54:51 +01:00
static void SetGfxFuncById(u8 funcId)
2019-08-01 19:01:50 +02:00
{
u8 i;
2021-03-01 07:54:51 +01:00
for (i = 0; i < ARRAY_COUNT(sGfxFuncs); i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
if (sGfxFuncs[i].id == funcId)
SetGfxFunc(sGfxFuncs[i].func);
2019-08-01 19:01:50 +02:00
}
}
2021-03-01 07:54:51 +01:00
static void Task_TryRunGfxFunc(u8 taskId)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
// Continue calling function until it
// has reached its finished state.
// Another will not be called until
// readied by SetGfxFunc
if (!sGfx->finished)
GetGfxFunc()();
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
static void LoadGfx(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
switch (sGfx->state)
2019-08-01 19:01:50 +02:00
{
case 0:
2021-03-01 07:54:51 +01:00
InitBgs();
sGfx->state++;
2019-08-01 19:01:50 +02:00
break;
case 1:
2021-03-01 07:54:51 +01:00
if (LoadBgGfx() == TRUE)
sGfx->state++;
2019-08-01 19:01:50 +02:00
break;
case 2:
2021-03-01 07:54:51 +01:00
CopyToBgTilemapBuffer(BG_SCENERY, sBg_Tilemap, 0, 0);
CopyToBgTilemapBuffer(BG_TREE_LEFT, sTreeBorderLeft_Tilemap, 0, 0);
CopyToBgTilemapBuffer(BG_TREE_RIGHT, sTreeBorderRight_Tilemap, 0, 0);
CopyBgTilemapBufferToVram(BG_SCENERY);
CopyBgTilemapBufferToVram(BG_TREE_LEFT);
CopyBgTilemapBufferToVram(BG_TREE_RIGHT);
sGfx->state++;
2019-08-01 19:01:50 +02:00
break;
case 3:
2021-03-01 07:54:51 +01:00
ShowBg(BG_INTERFACE);
ShowBg(BG_SCENERY);
ShowBg(BG_TREE_LEFT);
ShowBg(BG_TREE_RIGHT);
sGfx->state++;
2019-08-01 19:01:50 +02:00
break;
case 4:
2021-03-01 07:54:51 +01:00
LoadWindowFrameGfx(gSaveBlock2Ptr->optionsWindowFrameType);
LoadUserWindowFrameGfx();
sGfx->state++;
2019-08-01 19:01:50 +02:00
break;
default:
2021-03-01 07:54:51 +01:00
sGfx->finished = TRUE;
2019-08-01 19:01:50 +02:00
break;
}
}
2021-03-01 07:54:51 +01:00
static void ShowNames(void)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, numPlayers, playerId, colorsId, *name;
2019-08-01 19:01:50 +02:00
u32 left;
struct WindowTemplate window;
2021-03-01 07:54:51 +01:00
const struct WinCoords *coords;
2019-08-01 19:01:50 +02:00
2021-03-01 07:54:51 +01:00
switch (sGfx->state)
2019-08-01 19:01:50 +02:00
{
case 0:
2021-03-01 07:54:51 +01:00
numPlayers = GetNumPlayers();
coords = sNameWindowCoords[numPlayers - 1];
window.bg = BG_INTERFACE;
2019-08-01 19:01:50 +02:00
window.width = 7;
window.height = 2;
2021-03-01 07:54:51 +01:00
window.paletteNum = 13;
2019-08-01 19:01:50 +02:00
window.baseBlock = 0x13;
2021-03-01 07:54:51 +01:00
for (i = 0; i < numPlayers; coords++, i++)
2019-08-01 19:01:50 +02:00
{
2021-04-10 04:39:34 +02:00
colorsId = COLORID_GRAY;
2021-03-01 07:54:51 +01:00
playerId = GetPlayerIdByPos(i);
2021-10-30 22:47:37 +02:00
left = (56 - GetStringWidth(FONT_NORMAL, GetPlayerName(playerId), -1)) / 2u;
2021-03-01 07:54:51 +01:00
window.tilemapLeft = coords->left;
window.tilemapTop = coords->top;
sGfx->windowIds[i] = AddWindow(&window);
ClearWindowTilemap(sGfx->windowIds[i]);
FillWindowPixelBuffer(sGfx->windowIds[i], PIXEL_FILL(1));
if (playerId == GetMultiplayerId())
colorsId = COLORID_BLUE;
name = GetPlayerName(playerId);
AddTextPrinterParameterized3(sGfx->windowIds[i], FONT_NORMAL, left, 1, sTextColorTable[colorsId], TEXT_SKIP_DRAW, name);
2021-11-03 20:29:18 +01:00
CopyWindowToVram(sGfx->windowIds[i], COPYWIN_GFX);
2019-08-01 19:01:50 +02:00
window.baseBlock += 0xE;
2021-03-01 07:54:51 +01:00
DrawMessageWindow(&window);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-01 19:01:50 +02:00
break;
case 1:
if (!IsDma3ManagerBusyWithBgCopy())
{
2021-03-01 07:54:51 +01:00
numPlayers = GetNumPlayers();
for (i = 0; i < numPlayers; i++)
PutWindowTilemap(sGfx->windowIds[i]);
CopyBgTilemapBufferToVram(BG_INTERFACE);
sGfx->state++;
2019-08-01 19:01:50 +02:00
}
break;
default:
2021-03-01 07:54:51 +01:00
if (++sGfx->state > 180)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
numPlayers = GetNumPlayers();
for (i = 0; i < numPlayers; i++)
2019-08-01 19:01:50 +02:00
{
2021-03-01 07:54:51 +01:00
ClearWindowTilemap(sGfx->windowIds[i]);
RemoveWindow(sGfx->windowIds[i]);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
FillBgTilemapBufferRect_Palette0(BG_INTERFACE, 0, 0, 0, 30, 20);
CopyBgTilemapBufferToVram(BG_INTERFACE);
sGfx->finished = TRUE;
2019-08-02 00:41:55 +02:00
}
break;
}
}
2021-03-01 07:54:51 +01:00
static void PrintRankedScores(u8 numPlayers_)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, ranking = 0, rankedPlayers = 0;
u8 numPlayers = numPlayers_; // Needed to match
2019-08-02 00:41:55 +02:00
u8 *name;
u32 x, numWidth;
u8 numString[32];
2021-03-01 07:54:51 +01:00
u8 playersByRanking[MAX_RFU_PLAYERS] = {0, 1, 2, 3, 4};
struct DodrioGame_ScoreResults temp, scoreResults[MAX_RFU_PLAYERS];
2019-08-02 00:41:55 +02:00
2021-03-01 07:54:51 +01:00
// Get all players scores and rankings
for (i = 0; i < numPlayers; i++)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
playersByRanking[i] = i;
GetScoreResults(&temp, i);
scoreResults[i] = temp;
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
// Sort player ids by ranking
if (GetHighestScore() != 0)
2019-08-02 00:41:55 +02:00
{
do
{
2021-03-01 07:54:51 +01:00
for (i = 0; i < numPlayers; i++)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
if (scoreResults[i].ranking == ranking)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
playersByRanking[rankedPlayers] = i;
rankedPlayers++;
2019-08-02 00:41:55 +02:00
}
}
2021-03-01 07:54:51 +01:00
ranking = rankedPlayers;
} while (rankedPlayers < numPlayers);
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
// Put any player with a score of 0 at lowest ranking
for (i = 0; i < numPlayers; i++)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
if (scoreResults[i].score == 0)
scoreResults[i].ranking = numPlayers - 1;
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
// Print text
2021-10-30 22:47:37 +02:00
x = 216 - GetStringWidth(FONT_NORMAL, gText_SpacePoints, 0);
2021-03-01 07:54:51 +01:00
for (i = 0; i < numPlayers; i++)
2019-08-02 00:41:55 +02:00
{
2021-04-10 04:39:34 +02:00
u8 colorsId = COLORID_GRAY;
2021-03-01 07:54:51 +01:00
u8 playerId = playersByRanking[i];
u32 points = scoreResults[playerId].score;
2019-08-02 00:41:55 +02:00
AddTextPrinterParameterized(sGfx->windowIds[1], FONT_NORMAL, sRankingTexts[scoreResults[playerId].ranking], 8, sRankingYCoords[i], TEXT_SKIP_DRAW, NULL);
2021-03-01 07:54:51 +01:00
if (playerId == GetMultiplayerId())
colorsId = COLORID_BLUE;
name = GetPlayerName(playerId);
AddTextPrinterParameterized3(sGfx->windowIds[1], FONT_NORMAL, 28, sRankingYCoords[i], sTextColorTable[colorsId], TEXT_SKIP_DRAW, name);
2019-08-02 00:41:55 +02:00
ConvertIntToDecimalStringN(numString, points, STR_CONV_MODE_LEFT_ALIGN, 7);
2021-10-30 22:47:37 +02:00
numWidth = GetStringWidth(FONT_NORMAL, numString, -1);
AddTextPrinterParameterized(sGfx->windowIds[1], FONT_NORMAL, numString, x - numWidth, sRankingYCoords[i], TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(sGfx->windowIds[1], FONT_NORMAL, gText_SpacePoints, x, sRankingYCoords[i], TEXT_SKIP_DRAW, NULL);
2019-08-02 00:41:55 +02:00
}
}
2021-03-01 07:54:51 +01:00
static void ShowResults(void)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
u8 i, j, prizeState, numPlayers = GetNumPlayers();
2019-08-02 00:41:55 +02:00
u8 *name;
u32 strWidth, x;
2021-03-01 07:54:51 +01:00
switch (sGfx->state)
2019-08-02 00:41:55 +02:00
{
case 0:
2021-03-01 07:54:51 +01:00
SetScoreResults();
sGfx->timer = 0;
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 1:
2021-03-01 07:54:51 +01:00
sGfx->windowIds[0] = AddWindow(&sWindowTemplates_Results[0]);
sGfx->windowIds[1] = AddWindow(&sWindowTemplates_Results[1]);
ClearWindowTilemap(sGfx->windowIds[0]);
ClearWindowTilemap(sGfx->windowIds[1]);
DrawMessageWindow(&sWindowTemplates_Results[0]);
DrawMessageWindow(&sWindowTemplates_Results[1]);
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 2:
2021-03-01 07:54:51 +01:00
FillWindowPixelBuffer(sGfx->windowIds[0], PIXEL_FILL(1));
FillWindowPixelBuffer(sGfx->windowIds[1], PIXEL_FILL(1));
2021-10-30 22:47:37 +02:00
strWidth = GetStringWidth(FONT_NORMAL, gText_BerryPickingResults, -1);
2019-08-02 00:41:55 +02:00
x = (224 - strWidth) / 2;
AddTextPrinterParameterized(sGfx->windowIds[0], FONT_NORMAL, gText_BerryPickingResults, x, 1, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(sGfx->windowIds[1], FONT_NORMAL, gText_10P30P50P50P, 68, 17, TEXT_SKIP_DRAW, NULL);
2021-03-01 07:54:51 +01:00
for (i = 0; i < numPlayers; i++)
2019-08-02 00:41:55 +02:00
{
2021-04-10 04:39:34 +02:00
u8 colorsId = COLORID_GRAY;
2019-08-02 00:41:55 +02:00
if (i == GetMultiplayerId())
2021-03-01 07:54:51 +01:00
colorsId = COLORID_BLUE;
2019-08-02 00:41:55 +02:00
2021-03-01 07:54:51 +01:00
name = GetPlayerName(i);
AddTextPrinterParameterized3(sGfx->windowIds[1], FONT_NORMAL, 0, sResultsYCoords[i], sTextColorTable[colorsId], TEXT_SKIP_DRAW, name);
2019-08-02 00:41:55 +02:00
for (j = 0; j < 4; j++)
{
u32 width;
2021-03-01 07:54:51 +01:00
u16 berriesPicked = Min(GetBerryResult(i, j), MAX_BERRIES);
u16 maxBerriesPicked = Min(GetHighestBerryResult(j), MAX_BERRIES);
2019-08-02 00:41:55 +02:00
2021-03-01 07:54:51 +01:00
ConvertIntToDecimalStringN(gStringVar4, berriesPicked, STR_CONV_MODE_LEFT_ALIGN, 4);
2021-10-30 22:47:37 +02:00
width = GetStringWidth(FONT_NORMAL, gStringVar4, -1);
// If player got the most of a berry type, highlight their number in red
2021-03-01 07:54:51 +01:00
if (maxBerriesPicked == berriesPicked && maxBerriesPicked != 0)
AddTextPrinterParameterized3(sGfx->windowIds[1], FONT_NORMAL, sResultsXCoords[j] - width, sResultsYCoords[i], sTextColorTable[COLORID_RED], TEXT_SKIP_DRAW, gStringVar4);
2019-08-02 00:41:55 +02:00
else
AddTextPrinterParameterized(sGfx->windowIds[1], FONT_NORMAL, gStringVar4, sResultsXCoords[j] - width, sResultsYCoords[i], TEXT_SKIP_DRAW, NULL);
2019-08-02 00:41:55 +02:00
}
}
2021-11-03 20:29:18 +01:00
CopyWindowToVram(sGfx->windowIds[0], COPYWIN_GFX);
CopyWindowToVram(sGfx->windowIds[1], COPYWIN_GFX);
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 3:
if (!IsDma3ManagerBusyWithBgCopy())
{
2021-03-01 07:54:51 +01:00
PutWindowTilemap(sGfx->windowIds[0]);
PutWindowTilemap(sGfx->windowIds[1]);
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
CopyBgTilemapBufferToVram(BG_INTERFACE);
SetBerryIconsInvisibility(FALSE);
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 4:
2021-03-01 07:54:51 +01:00
if (++sGfx->timer >= 30 && JOY_NEW(A_BUTTON))
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
sGfx->timer = 0;
2019-08-02 00:41:55 +02:00
PlaySE(SE_SELECT);
2021-03-01 07:54:51 +01:00
SetBerryIconsInvisibility(TRUE);
sGfx->state++;
2019-08-02 00:41:55 +02:00
}
break;
case 5:
2021-03-01 07:54:51 +01:00
FillWindowPixelBuffer(sGfx->windowIds[0], PIXEL_FILL(1));
FillWindowPixelBuffer(sGfx->windowIds[1], PIXEL_FILL(1));
2021-10-30 22:47:37 +02:00
strWidth = GetStringWidth(FONT_NORMAL, gText_AnnouncingRankings, -1);
2019-08-02 00:41:55 +02:00
x = (224 - strWidth) / 2;
AddTextPrinterParameterized(sGfx->windowIds[0], FONT_NORMAL, gText_AnnouncingRankings, x, 1, TEXT_SKIP_DRAW, NULL);
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 6:
2021-03-01 07:54:51 +01:00
PrintRankedScores(numPlayers);
2021-11-03 20:29:18 +01:00
CopyWindowToVram(sGfx->windowIds[0], COPYWIN_GFX);
CopyWindowToVram(sGfx->windowIds[1], COPYWIN_GFX);
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 7:
if (!IsDma3ManagerBusyWithBgCopy())
{
2021-03-01 07:54:51 +01:00
PutWindowTilemap(sGfx->windowIds[0]);
PutWindowTilemap(sGfx->windowIds[1]);
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
CopyBgTilemapBufferToVram(BG_INTERFACE);
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 8:
2021-03-01 07:54:51 +01:00
if (++sGfx->timer >= 30 && JOY_NEW(A_BUTTON))
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
sGfx->timer = 0;
2019-08-02 00:41:55 +02:00
PlaySE(SE_SELECT);
2021-03-01 07:54:51 +01:00
if (GetHighestScore() < PRIZE_SCORE)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
sGfx->state = 127; // Skip to end, past giving prize
2019-08-02 00:41:55 +02:00
}
else
{
StopMapMusic();
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
FillBgTilemapBufferRect_Palette0(BG_INTERFACE, 0, 0, 5, 30, 15);
RemoveWindow(sGfx->windowIds[1]);
sGfx->windowIds[1] = AddWindow(&sWindowTemplate_Prize);
ClearWindowTilemap(sGfx->windowIds[1]);
DrawMessageWindow(&sWindowTemplate_Prize);
2019-08-02 00:41:55 +02:00
}
break;
case 9:
2020-08-21 00:02:00 +02:00
PlayNewMapMusic(MUS_LEVEL_UP);
2021-03-01 07:54:51 +01:00
FillWindowPixelBuffer(sGfx->windowIds[0], PIXEL_FILL(1));
FillWindowPixelBuffer(sGfx->windowIds[1], PIXEL_FILL(1));
2021-10-30 22:47:37 +02:00
strWidth = GetStringWidth(FONT_NORMAL, gText_AnnouncingPrizes, -1);
2019-08-02 00:41:55 +02:00
x = (224 - strWidth) / 2;
AddTextPrinterParameterized(sGfx->windowIds[0], FONT_NORMAL, gText_AnnouncingPrizes, x, 1, TEXT_SKIP_DRAW, NULL);
2019-08-02 00:41:55 +02:00
DynamicPlaceholderTextUtil_Reset();
2021-03-01 07:54:51 +01:00
CopyItemName(GetPrizeItemId(), gStringVar1);
2019-08-02 00:41:55 +02:00
DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, gStringVar1);
DynamicPlaceholderTextUtil_ExpandPlaceholders(gStringVar4, gText_FirstPlacePrize);
AddTextPrinterParameterized(sGfx->windowIds[1], FONT_NORMAL, gStringVar4, 0, 1, TEXT_SKIP_DRAW, NULL);
2021-03-01 07:54:51 +01:00
prizeState = TryGivePrize();
if (prizeState != PRIZE_RECEIVED && prizeState != NO_PRIZE)
2019-08-02 00:41:55 +02:00
{
DynamicPlaceholderTextUtil_Reset();
2021-03-01 07:54:51 +01:00
CopyItemName(GetPrizeItemId(), gStringVar1);
2019-08-02 00:41:55 +02:00
DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, gStringVar1);
2021-03-01 07:54:51 +01:00
if (prizeState == PRIZE_NO_ROOM)
2019-08-02 00:41:55 +02:00
DynamicPlaceholderTextUtil_ExpandPlaceholders(gStringVar4, gText_CantHoldAnyMore);
2021-03-01 07:54:51 +01:00
else if (prizeState == PRIZE_FILLED_BAG)
2019-08-02 00:41:55 +02:00
DynamicPlaceholderTextUtil_ExpandPlaceholders(gStringVar4, gText_FilledStorageSpace);
AddTextPrinterParameterized(sGfx->windowIds[1], FONT_NORMAL, gStringVar4, 0, 41, TEXT_SKIP_DRAW, NULL);
2019-08-02 00:41:55 +02:00
}
2021-11-03 20:29:18 +01:00
CopyWindowToVram(sGfx->windowIds[0], COPYWIN_GFX);
CopyWindowToVram(sGfx->windowIds[1], COPYWIN_GFX);
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 10:
if (!IsDma3ManagerBusyWithBgCopy())
{
2021-03-01 07:54:51 +01:00
PutWindowTilemap(sGfx->windowIds[0]);
PutWindowTilemap(sGfx->windowIds[1]);
2019-08-01 19:01:50 +02:00
}
2021-03-01 07:54:51 +01:00
CopyBgTilemapBufferToVram(BG_INTERFACE);
2020-08-21 00:02:00 +02:00
FadeOutAndFadeInNewMapMusic(MUS_RG_VICTORY_WILD, 20, 10);
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 11:
2021-03-01 07:54:51 +01:00
if (++sGfx->timer >= 30 && JOY_NEW(A_BUTTON))
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
sGfx->timer = 0;
2019-08-02 00:41:55 +02:00
PlaySE(SE_SELECT);
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
}
break;
default:
2021-03-01 07:54:51 +01:00
ClearWindowTilemap(sGfx->windowIds[0]);
ClearWindowTilemap(sGfx->windowIds[1]);
RemoveWindow(sGfx->windowIds[0]);
RemoveWindow(sGfx->windowIds[1]);
FillBgTilemapBufferRect_Palette0(BG_INTERFACE, 0, 0, 0, 30, 20);
CopyBgTilemapBufferToVram(BG_INTERFACE);
sGfx->finished = TRUE;
2019-08-01 19:01:50 +02:00
break;
}
2019-07-31 00:27:30 +02:00
}
2019-08-02 00:41:55 +02:00
2021-03-01 07:54:51 +01:00
static void Msg_WantToPlayAgain(void)
2019-08-02 00:41:55 +02:00
{
u8 y;
2021-03-01 07:54:51 +01:00
switch (sGfx->state)
2019-08-02 00:41:55 +02:00
{
case 0:
2021-03-01 07:54:51 +01:00
// Create windows
sGfx->windowIds[WIN_PLAY_AGAIN] = AddWindow(&sWindowTemplates_PlayAgain[WIN_PLAY_AGAIN]);
sGfx->windowIds[WIN_YES_NO] = AddWindow(&sWindowTemplates_PlayAgain[WIN_YES_NO]);
ClearWindowTilemap(sGfx->windowIds[WIN_PLAY_AGAIN]);
ClearWindowTilemap(sGfx->windowIds[WIN_YES_NO]);
DrawMessageWindow(&sWindowTemplates_PlayAgain[WIN_PLAY_AGAIN]);
DrawYesNoMessageWindow(&sWindowTemplates_PlayAgain[WIN_YES_NO]);
sGfx->state++;
sGfx->cursorSelection = PLAY_AGAIN_NONE;
sGfx->playAgainState = PLAY_AGAIN_NONE;
2019-08-02 00:41:55 +02:00
break;
case 1:
2021-03-01 07:54:51 +01:00
// Print text
FillWindowPixelBuffer(sGfx->windowIds[WIN_PLAY_AGAIN], PIXEL_FILL(1));
FillWindowPixelBuffer(sGfx->windowIds[WIN_YES_NO], PIXEL_FILL(1));
AddTextPrinterParameterized(sGfx->windowIds[WIN_PLAY_AGAIN], FONT_NORMAL, gText_WantToPlayAgain, 0, 5, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(sGfx->windowIds[WIN_YES_NO], FONT_NORMAL, gText_Yes, 8, 1, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(sGfx->windowIds[WIN_YES_NO], FONT_NORMAL, gText_No, 8, 17, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(sGfx->windowIds[WIN_YES_NO], FONT_NORMAL, gText_SelectorArrow2, 0, 1, TEXT_SKIP_DRAW, NULL);
2021-11-03 20:29:18 +01:00
CopyWindowToVram(sGfx->windowIds[WIN_PLAY_AGAIN], COPYWIN_GFX);
CopyWindowToVram(sGfx->windowIds[WIN_YES_NO], COPYWIN_GFX);
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 2:
2021-03-01 07:54:51 +01:00
// Draw windows
2019-08-02 00:41:55 +02:00
if (!IsDma3ManagerBusyWithBgCopy())
{
2021-03-01 07:54:51 +01:00
PutWindowTilemap(sGfx->windowIds[WIN_PLAY_AGAIN]);
PutWindowTilemap(sGfx->windowIds[WIN_YES_NO]);
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
CopyBgTilemapBufferToVram(BG_INTERFACE);
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 3:
2021-03-01 07:54:51 +01:00
// Handle input
y = sGfx->cursorSelection;
if (y == PLAY_AGAIN_NONE)
y = PLAY_AGAIN_YES;
FillWindowPixelBuffer(sGfx->windowIds[WIN_YES_NO], PIXEL_FILL(1));
AddTextPrinterParameterized(sGfx->windowIds[WIN_YES_NO], FONT_NORMAL, gText_Yes, 8, 1, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(sGfx->windowIds[WIN_YES_NO], FONT_NORMAL, gText_No, 8, 17, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(sGfx->windowIds[WIN_YES_NO], FONT_NORMAL, gText_SelectorArrow2, 0, ((y - 1) * 16) + 1, TEXT_SKIP_DRAW, NULL);
2021-11-03 20:29:18 +01:00
CopyWindowToVram(sGfx->windowIds[WIN_YES_NO], COPYWIN_FULL);
2019-08-02 00:41:55 +02:00
// Increment state only if A or B button have been pressed.
2020-09-05 03:11:55 +02:00
if (JOY_NEW(A_BUTTON))
2019-08-02 00:41:55 +02:00
{
PlaySE(SE_SELECT);
2021-03-01 07:54:51 +01:00
if (sGfx->cursorSelection == PLAY_AGAIN_NONE)
sGfx->cursorSelection = PLAY_AGAIN_YES;
sGfx->state++;
2019-08-02 00:41:55 +02:00
}
2020-09-05 03:11:55 +02:00
else if (JOY_NEW(DPAD_UP | DPAD_DOWN))
2019-08-02 00:41:55 +02:00
{
PlaySE(SE_SELECT);
2021-03-01 07:54:51 +01:00
switch (sGfx->cursorSelection)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
case PLAY_AGAIN_NONE:
sGfx->cursorSelection = PLAY_AGAIN_NO;
2019-08-02 00:41:55 +02:00
break;
2021-03-01 07:54:51 +01:00
case PLAY_AGAIN_YES:
sGfx->cursorSelection = PLAY_AGAIN_NO;
2019-08-02 00:41:55 +02:00
break;
2021-03-01 07:54:51 +01:00
case PLAY_AGAIN_NO:
sGfx->cursorSelection = PLAY_AGAIN_YES;
2019-08-02 00:41:55 +02:00
break;
}
}
2020-09-05 03:11:55 +02:00
else if (JOY_NEW(B_BUTTON))
2019-08-02 00:41:55 +02:00
{
PlaySE(SE_SELECT);
2021-03-01 07:54:51 +01:00
sGfx->cursorSelection = PLAY_AGAIN_NO;
sGfx->state++;
2019-08-02 00:41:55 +02:00
}
break;
default:
2021-03-01 07:54:51 +01:00
sGfx->playAgainState = sGfx->cursorSelection;
ClearWindowTilemap(sGfx->windowIds[WIN_PLAY_AGAIN]);
ClearWindowTilemap(sGfx->windowIds[WIN_YES_NO]);
RemoveWindow(sGfx->windowIds[WIN_PLAY_AGAIN]);
RemoveWindow(sGfx->windowIds[WIN_YES_NO]);
FillBgTilemapBufferRect_Palette0(BG_INTERFACE, 0, 0, 0, 30, 20);
CopyBgTilemapBufferToVram(BG_INTERFACE);
sGfx->finished = TRUE;
2019-08-02 00:41:55 +02:00
break;
}
}
2021-03-01 07:54:51 +01:00
static void Msg_SavingDontTurnOff(void)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
switch (sGfx->state)
2019-08-02 00:41:55 +02:00
{
case 0:
DrawDialogueFrame(0, FALSE);
AddTextPrinterParameterized2(0, FONT_NORMAL, gText_SavingDontTurnOffPower, 0, NULL, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY);
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 1:
2021-11-03 20:29:18 +01:00
CopyWindowToVram(0, COPYWIN_FULL);
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 2:
if (!IsDma3ManagerBusyWithBgCopy())
{
2021-10-29 04:54:41 +02:00
CreateTask(Task_LinkFullSave, 0);
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
}
break;
case 3:
2021-10-29 04:54:41 +02:00
if (!FuncIsActiveTask(Task_LinkFullSave))
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
default:
2021-03-01 07:54:51 +01:00
FillBgTilemapBufferRect_Palette0(BG_INTERFACE, 0, 0, 0, 30, 20);
CopyBgTilemapBufferToVram(BG_INTERFACE);
sGfx->finished = TRUE;
2019-08-02 00:41:55 +02:00
break;
}
}
2021-03-01 07:54:51 +01:00
static void Msg_CommunicationStandby(void)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
switch (sGfx->state)
2019-08-02 00:41:55 +02:00
{
case 0:
2021-03-01 07:54:51 +01:00
sGfx->windowIds[0] = AddWindow(&sWindowTemplate_CommStandby);
ClearWindowTilemap(sGfx->windowIds[0]);
DrawMessageWindow(&sWindowTemplate_CommStandby);
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 1:
2021-03-01 07:54:51 +01:00
FillWindowPixelBuffer(sGfx->windowIds[0], PIXEL_FILL(1));
AddTextPrinterParameterized(sGfx->windowIds[0], FONT_NORMAL, gText_CommunicationStandby3, 0, 5, TEXT_SKIP_DRAW, NULL);
2021-11-03 20:29:18 +01:00
CopyWindowToVram(sGfx->windowIds[0], COPYWIN_GFX);
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 2:
if (!IsDma3ManagerBusyWithBgCopy())
2021-03-01 07:54:51 +01:00
PutWindowTilemap(sGfx->windowIds[0]);
CopyBgTilemapBufferToVram(BG_INTERFACE);
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
default:
2021-03-01 07:54:51 +01:00
sGfx->finished = TRUE;
2019-08-02 00:41:55 +02:00
break;
}
}
2021-03-01 07:54:51 +01:00
static void EraseMessage(void)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
ClearWindowTilemap(sGfx->windowIds[0]);
RemoveWindow(sGfx->windowIds[0]);
FillBgTilemapBufferRect_Palette0(BG_INTERFACE, 0, 0, 0, 30, 20);
CopyBgTilemapBufferToVram(BG_INTERFACE);
sGfx->finished = TRUE;
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
static void Msg_SomeoneDroppedOut(void)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
switch (sGfx->state)
2019-08-02 00:41:55 +02:00
{
case 0:
2021-03-01 07:54:51 +01:00
sGfx->windowIds[0] = AddWindow(&sWindowTemplate_DroppedOut);
ClearWindowTilemap(sGfx->windowIds[0]);
DrawMessageWindow(&sWindowTemplate_DroppedOut);
sGfx->state++;
sGfx->timer = 0;
sGfx->cursorSelection = 0;
sGfx->playAgainState = PLAY_AGAIN_NONE;
2019-08-02 00:41:55 +02:00
break;
case 1:
2021-03-01 07:54:51 +01:00
FillWindowPixelBuffer(sGfx->windowIds[0], PIXEL_FILL(1));
AddTextPrinterParameterized(sGfx->windowIds[0], FONT_NORMAL, gText_SomeoneDroppedOut, 0, 5, TEXT_SKIP_DRAW, NULL);
2021-11-03 20:29:18 +01:00
CopyWindowToVram(sGfx->windowIds[0], COPYWIN_GFX);
2021-03-01 07:54:51 +01:00
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 2:
if (!IsDma3ManagerBusyWithBgCopy())
2021-03-01 07:54:51 +01:00
PutWindowTilemap(sGfx->windowIds[0]);
CopyBgTilemapBufferToVram(BG_INTERFACE);
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
case 3:
2021-03-01 07:54:51 +01:00
if (++sGfx->timer >= 120)
sGfx->state++;
2019-08-02 00:41:55 +02:00
break;
default:
2021-03-01 07:54:51 +01:00
sGfx->playAgainState = PLAY_AGAIN_DROPPED;
ClearWindowTilemap(sGfx->windowIds[0]);
RemoveWindow(sGfx->windowIds[0]);
FillBgTilemapBufferRect_Palette0(BG_INTERFACE, 0, 0, 0, 30, 20);
CopyBgTilemapBufferToVram(BG_INTERFACE);
sGfx->finished = TRUE;
2019-08-02 00:41:55 +02:00
break;
}
}
2021-03-01 07:54:51 +01:00
static void StopGfxFuncs(void)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
DestroyTask(sGfx->taskId);
sGfx->finished = TRUE;
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
static void GfxIdle(void)
2019-08-02 00:41:55 +02:00
{
}
2021-03-01 07:54:51 +01:00
static void SetGfxFunc(void (*func)(void))
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
sGfx->state = 0;
sGfx->finished = FALSE;
sGfx->func = func;
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
static void (*GetGfxFunc(void))(void)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
return sGfx->func;
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
static bool32 IsGfxFuncActive(void)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
if (sGfx->finished == TRUE)
2019-08-02 00:41:55 +02:00
return FALSE;
else
return TRUE;
}
2021-03-01 07:54:51 +01:00
static u8 GetPlayAgainState(void)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
return sGfx->playAgainState;
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
static void InitBgs(void)
2019-08-02 00:41:55 +02:00
{
DmaClearLarge16(3, (void *)VRAM, VRAM_SIZE, 0x1000);
DmaClear32(3,(void *)OAM, OAM_SIZE);
DmaClear16(3, (void *)PLTT, PLTT_SIZE);
SetGpuReg(REG_OFFSET_DISPCNT, 0);
ResetBgsAndClearDma3BusyFlags(0);
2021-03-01 07:54:51 +01:00
InitBgsFromTemplates(0, sBgTemplates, ARRAY_COUNT(sBgTemplates));
2021-11-04 04:02:06 +01:00
ChangeBgX(0, 0, BG_COORD_SET);
ChangeBgY(0, 0, BG_COORD_SET);
ChangeBgX(1, 0, BG_COORD_SET);
ChangeBgY(1, 0, BG_COORD_SET);
ChangeBgX(2, 0, BG_COORD_SET);
ChangeBgY(2, 0, BG_COORD_SET);
ChangeBgX(3, 0, BG_COORD_SET);
ChangeBgY(3, 0, BG_COORD_SET);
2019-08-02 00:41:55 +02:00
InitStandardTextBoxWindows();
InitTextBoxGfxAndPrinters();
2019-08-02 00:41:55 +02:00
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP);
2021-03-01 07:54:51 +01:00
SetBgTilemapBuffer(BG_SCENERY, sGfx->tilemapBuffers[0]);
SetBgTilemapBuffer(BG_TREE_LEFT, sGfx->tilemapBuffers[1]);
SetBgTilemapBuffer(BG_TREE_RIGHT, sGfx->tilemapBuffers[2]);
2019-08-02 00:41:55 +02:00
}
2021-03-01 07:54:51 +01:00
static bool32 LoadBgGfx(void)
2019-08-02 00:41:55 +02:00
{
2021-03-01 07:54:51 +01:00
switch (sGfx->loadState)
2019-08-02 00:41:55 +02:00
{
case 0:
2021-03-01 07:54:51 +01:00
LoadPalette(sBg_Pal, 0, sizeof(sBg_Pal));
2019-08-02 00:41:55 +02:00
break;
case 1:
2020-05-14 10:37:09 +02:00
ResetTempTileDataBuffers();
2019-08-02 00:41:55 +02:00
break;
case 2:
2021-03-01 07:54:51 +01:00
DecompressAndCopyTileDataToVram(BG_SCENERY, sBg_Gfx, 0, 0, 0);
2019-08-02 00:41:55 +02:00
break;
case 3:
2021-03-01 07:54:51 +01:00
DecompressAndCopyTileDataToVram(BG_TREE_LEFT, sTreeBorder_Gfx, 0, 0, 0);
2019-08-02 00:41:55 +02:00
break;
case 4:
2020-05-14 10:37:09 +02:00
if (FreeTempTileDataBuffersIfPossible() == TRUE)
2019-08-02 00:41:55 +02:00
return FALSE;
break;
case 5:
2020-08-07 08:00:41 +02:00
LoadPalette(GetTextWindowPalette(3), 0xD0, 0x20);
2019-08-02 00:41:55 +02:00
break;
default:
2021-03-01 07:54:51 +01:00
sGfx->loadState = 0;
2019-08-02 00:41:55 +02:00
return TRUE;
}
2021-03-01 07:54:51 +01:00
sGfx->loadState++;
2019-08-02 00:41:55 +02:00
return FALSE;
}