mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2025-01-07 10:03:18 +01:00
4243 lines
117 KiB
C
Executable File
4243 lines
117 KiB
C
Executable File
#include "global.h"
|
|
#include "malloc.h"
|
|
#include "battle_anim.h"
|
|
#include "bg.h"
|
|
#include "data.h"
|
|
#include "decompress.h"
|
|
#include "dynamic_placeholder_text_util.h"
|
|
#include "event_data.h"
|
|
#include "international_string_util.h"
|
|
#include "item.h"
|
|
#include "link.h"
|
|
#include "link_rfu.h"
|
|
#include "main.h"
|
|
#include "menu.h"
|
|
#include "minigame_countdown.h"
|
|
#include "palette.h"
|
|
#include "random.h"
|
|
#include "digit_obj_util.h"
|
|
#include "save.h"
|
|
#include "script.h"
|
|
#include "sound.h"
|
|
#include "sprite.h"
|
|
#include "string_util.h"
|
|
#include "strings.h"
|
|
#include "task.h"
|
|
#include "text_window.h"
|
|
#include "trig.h"
|
|
#include "pokemon.h"
|
|
#include "pokemon_jump.h"
|
|
#include "constants/rgb.h"
|
|
#include "constants/songs.h"
|
|
#include "constants/items.h"
|
|
|
|
#define MAX_JUMP_SCORE 99990
|
|
#define MAX_JUMPS 9999
|
|
|
|
#define JUMP_PEAK (-30)
|
|
|
|
enum {
|
|
BG_INTERFACE,
|
|
BG_BONUSES,
|
|
BG_VENUSAUR,
|
|
BG_SCENERY,
|
|
};
|
|
|
|
// IDs for persistent windows
|
|
enum {
|
|
WIN_POINTS,
|
|
WIN_TIMES,
|
|
NUM_WINDOWS
|
|
};
|
|
|
|
enum {
|
|
PACKET_MON_INFO = 1,
|
|
PACKET_UNUSED,
|
|
PACKET_LEADER_STATE,
|
|
PACKET_MEMBER_STATE,
|
|
};
|
|
|
|
enum {
|
|
JUMP_TYPE_NORMAL,
|
|
JUMP_TYPE_FAST,
|
|
JUMP_TYPE_SLOW,
|
|
};
|
|
|
|
enum {
|
|
FUNC_GAME_INTRO,
|
|
FUNC_WAIT_ROUND,
|
|
FUNC_GAME_ROUND,
|
|
FUNC_GAME_OVER,
|
|
FUNC_ASK_PLAY_AGAIN,
|
|
FUNC_RESET_GAME,
|
|
FUNC_EXIT,
|
|
FUNC_GIVE_PRIZE,
|
|
FUNC_SAVE,
|
|
FUNC_NONE
|
|
};
|
|
|
|
enum {
|
|
GFXFUNC_LOAD,
|
|
GFXFUNC_SHOW_NAMES,
|
|
GFXFUNC_SHOW_NAMES_HIGHLIGHT,
|
|
GFXFUNC_ERASE_NAMES,
|
|
GFXFUNC_MSG_PLAY_AGAIN,
|
|
GFXFUNC_MSG_SAVING,
|
|
GFXFUNC_ERASE_MSG,
|
|
GFXFUNC_MSG_PLAYER_DROPPED,
|
|
GFXFUNC_MSG_COMM_STANDBY,
|
|
GFXFUNC_COUNTDOWN,
|
|
};
|
|
|
|
enum {
|
|
VINE_HIGHEST,
|
|
VINE_DOWNSWING_HIGHER,
|
|
VINE_DOWNSWING_HIGH,
|
|
VINE_DOWNSWING_LOW,
|
|
VINE_DOWNSWING_LOWER,
|
|
VINE_LOWEST,
|
|
VINE_UPSWING_LOWER,
|
|
VINE_UPSWING_LOW,
|
|
VINE_UPSWING_HIGH,
|
|
VINE_UPSWING_HIGHER,
|
|
NUM_VINESTATES
|
|
};
|
|
|
|
// Used to compare limits for vineStateTimer
|
|
// The upper 8 bits of vineStateTimer are the vine state,
|
|
// the lower 8 bits are a timer to the next state.
|
|
// When the timer is incremented above 255, it increments
|
|
// the vine state and the timer is reset.
|
|
#define VINE_STATE_TIMER(vineState)(((vineState) << 8) | 0xFF)
|
|
|
|
enum {
|
|
MONSTATE_NORMAL, // Pokémon is either on the ground or in the middle of a jump
|
|
MONSTATE_JUMP, // Pokémon has begun a jump
|
|
MONSTATE_HIT, // Pokémon got hit by the vine
|
|
};
|
|
|
|
enum {
|
|
JUMPSTATE_NONE,
|
|
JUMPSTATE_SUCCESS, // Cleared vine
|
|
JUMPSTATE_FAILURE, // Hit vine
|
|
};
|
|
|
|
#define PLAY_AGAIN_NO 1
|
|
#define PLAY_AGAIN_YES 2
|
|
|
|
#define TAG_MON1 0
|
|
#define TAG_MON2 1 // MON2-5 used implicitly by adding multiplayer id to tag
|
|
#define TAG_MON3 2
|
|
#define TAG_MON4 3
|
|
#define TAG_MON5 4
|
|
|
|
#define GFXTAG_VINE1 5
|
|
#define GFXTAG_VINE2 6
|
|
#define GFXTAG_VINE3 7
|
|
#define GFXTAG_VINE4 8
|
|
#define GFXTAG_COUNTDOWN 9
|
|
#define GFXTAG_STAR 10
|
|
|
|
#define PALTAG_1 5
|
|
#define PALTAG_2 6
|
|
#define PALTAG_COUNTDOWN 7
|
|
|
|
#define TAG_DIGITS 800
|
|
|
|
#define VINE_SPRITES_PER_SIDE 4 // Vine rope is divided into 8 sprites, 4 per side copied and flipped horizontally
|
|
|
|
// Used by SetLinkTimeInterval to get a bit mask for capping
|
|
// a timer that controls how frequently link data is sent.
|
|
#define LINK_INTERVAL_NONE 0
|
|
#define LINK_INTERVAL_SHORT 3 // 3 frame interval
|
|
#define LINK_INTERVAL_MEDIUM 4 // 7 frame interval
|
|
#define LINK_INTERVAL_LONG 5 // 15 frame interval
|
|
|
|
#define LINK_TIMER_STOPPED 0x1111
|
|
|
|
struct PokemonJump_MonInfo
|
|
{
|
|
u16 species;
|
|
u32 otId;
|
|
u32 personality;
|
|
};
|
|
|
|
struct PokemonJump_Player
|
|
{
|
|
int jumpOffset;
|
|
int jumpOffsetIdx;
|
|
u32 unused;
|
|
u16 monJumpType;
|
|
u16 jumpTimeStart;
|
|
u16 monState;
|
|
u16 prevMonState;
|
|
int jumpState;
|
|
bool32 funcFinished;
|
|
u8 name[11];
|
|
};
|
|
|
|
struct PokemonJumpGfx
|
|
{
|
|
bool32 funcFinished;
|
|
u16 mainState;
|
|
u8 taskId;
|
|
u8 unused1[3];
|
|
u8 resetVineState;
|
|
u8 resetVineTimer;
|
|
u8 vineState;
|
|
u8 msgWindowState;
|
|
u8 vinePalNumDownswing;
|
|
u8 vinePalNumUpswing;
|
|
u16 unused2;
|
|
u16 msgWindowId;
|
|
u16 fanfare;
|
|
u32 bonusTimer;
|
|
u16 nameWindowIds[MAX_RFU_PLAYERS];
|
|
u8 itemName[64];
|
|
u8 itemQuantityStr[64];
|
|
u8 prizeMsg[256];
|
|
u16 tilemapBuffer[0x4000];
|
|
struct Sprite *monSprites[MAX_RFU_PLAYERS];
|
|
struct Sprite *starSprites[MAX_RFU_PLAYERS];
|
|
struct Sprite *vineSprites[VINE_SPRITES_PER_SIDE * 2];
|
|
u8 unused3[12];
|
|
u8 monSpriteSubpriorities[MAX_RFU_PLAYERS];
|
|
};
|
|
|
|
struct PokemonJump_CommData
|
|
{
|
|
u8 funcId;
|
|
u8 receivedBonusFlags;
|
|
u16 data; // Multi-use
|
|
u16 jumpsInRow;
|
|
u32 jumpScore;
|
|
};
|
|
|
|
struct PokemonJump
|
|
{
|
|
MainCallback exitCallback;
|
|
u8 taskId;
|
|
u8 numPlayers;
|
|
u8 multiplayerId;
|
|
u8 startDelayTimer;
|
|
u16 mainState;
|
|
u16 helperState;
|
|
u16 excellentsInRow;
|
|
u16 excellentsInRowRecord;
|
|
bool32 gameOver;
|
|
u32 vineState;
|
|
u32 prevVineState;
|
|
int vineSpeed;
|
|
u32 vineSpeedAccel;
|
|
u32 rngSeed;
|
|
u32 nextVineSpeed;
|
|
int linkTimer;
|
|
u32 linkTimerLimit;
|
|
u16 vineStateTimer;
|
|
bool16 ignoreJumpInput;
|
|
u16 unused1;
|
|
u16 unused2; // Set to 0, never read
|
|
u16 timer;
|
|
u16 prizeItemId;
|
|
u16 prizeItemQuantity;
|
|
u16 playAgainComm;
|
|
u8 unused3; // Set to 0, never read
|
|
u8 playAgainState;
|
|
bool8 allowVineUpdates;
|
|
bool8 isLeader;
|
|
bool8 funcActive;
|
|
bool8 allPlayersReady;
|
|
u16 vineTimer;
|
|
u8 nextFuncId;
|
|
bool8 showBonus;
|
|
u16 vineSpeedDelay;
|
|
u8 vineBaseSpeedIdx;
|
|
u8 vineSpeedStage;
|
|
int numPlayersAtPeak;
|
|
bool32 initScoreUpdate;
|
|
bool32 updateScore;
|
|
bool32 unused4; // Set to TRUE, never read
|
|
bool32 giveBonus;
|
|
bool32 skipJumpUpdate;
|
|
bool32 atMaxSpeedStage;
|
|
struct PokemonJump_CommData comm;
|
|
bool8 atJumpPeak[MAX_RFU_PLAYERS];
|
|
bool8 atJumpPeak2[MAX_RFU_PLAYERS];
|
|
bool8 atJumpPeak3[MAX_RFU_PLAYERS];
|
|
u8 memberFuncIds[MAX_RFU_PLAYERS];
|
|
u16 playAgainStates[MAX_RFU_PLAYERS];
|
|
u16 jumpTimeStarts[MAX_RFU_PLAYERS];
|
|
struct PokemonJumpGfx jumpGfx;
|
|
struct PokemonJump_MonInfo monInfo[MAX_RFU_PLAYERS];
|
|
struct PokemonJump_Player players[MAX_RFU_PLAYERS];
|
|
struct PokemonJump_Player *player;
|
|
};
|
|
|
|
struct PokemonJumpMons
|
|
{
|
|
u16 species;
|
|
u16 jumpType;
|
|
};
|
|
|
|
static void InitGame(struct PokemonJump *);
|
|
static void ResetForNewGame(struct PokemonJump *);
|
|
static void InitPlayerAndJumpTypes(void);
|
|
static void ResetPlayersForNewGame(void);
|
|
static s16 GetPokemonJumpSpeciesIdx(u16 species);
|
|
static void InitJumpMonInfo(struct PokemonJump_MonInfo *, struct Pokemon *);
|
|
static void CB2_PokemonJump(void);
|
|
static void Task_StartPokemonJump(u8);
|
|
static void Task_PokemonJump_Leader(u8);
|
|
static void SendLinkData_Leader(void);
|
|
static void Task_PokemonJump_Member(u8);
|
|
static void SendLinkData_Member(void);
|
|
static bool32 GameIntro_Leader(void);
|
|
static bool32 WaitRound_Leader(void);
|
|
static bool32 GameRound_Leader(void);
|
|
static bool32 GameOver_Leader(void);
|
|
static bool32 GameOver_Member(void);
|
|
static bool32 AskPlayAgain_Leader(void);
|
|
static bool32 AskPlayAgain_Member(void);
|
|
static bool32 ResetGame_Leader(void);
|
|
static bool32 ResetGame_Member(void);
|
|
static bool32 ExitGame(void);
|
|
static bool32 GivePrize_Leader(void);
|
|
static bool32 GivePrize_Member(void);
|
|
static bool32 SavePokeJump(void);
|
|
static bool32 DoGameIntro(void);
|
|
static bool32 HandleSwingRound(void);
|
|
static bool32 DoVineHitEffect(void);
|
|
static bool32 GameIntro_Member(void);
|
|
static bool32 WaitRound_Member(void);
|
|
static bool32 GameRound_Member(void);
|
|
static bool32 TryGivePrize(void);
|
|
static bool32 DoPlayAgainPrompt(void);
|
|
static bool32 ClosePokeJumpLink(void);
|
|
static bool32 CloseMessageAndResetScore(void);
|
|
static void Task_CommunicateMonInfo(u8);
|
|
static void SetTaskWithPokeJumpStruct(TaskFunc, u8);
|
|
static void InitVineState(void);
|
|
static void ResetVineState(void);
|
|
static void UpdateVineState(void);
|
|
static int GetVineSpeed(void);
|
|
static void UpdateVineSpeed(void);
|
|
static int PokeJumpRandom(void);
|
|
static void ResetVineAfterHit(void);
|
|
static void ResetPlayersJumpStates(void);
|
|
static void ResetPlayersMonState(void);
|
|
static bool32 IsPlayersMonState(u16);
|
|
static void SetMonStateJump(void);
|
|
static void UpdateGame(void);
|
|
static void TryUpdateVineSwing(void);
|
|
static void DisallowVineUpdates(void);
|
|
static void AllowVineUpdates(void);
|
|
static void HandleMonState(void);
|
|
static void UpdateJump(int);
|
|
static void TryUpdateScore(void);
|
|
static bool32 UpdateVineHitStates(void);
|
|
static bool32 AllPlayersJumpedOrHit(void);
|
|
static bool32 DidAllPlayersClearVine(void);
|
|
static bool32 ShouldPlayAgain(void);
|
|
static void AddJumpScore(int);
|
|
static int GetPlayersAtJumpPeak(void);
|
|
static bool32 AreLinkQueuesEmpty(void);
|
|
static int GetNumPlayersForBonus(u8 *);
|
|
static void ClearUnreadField(void);
|
|
static int GetScoreBonus(int);
|
|
static void TryUpdateExcellentsRecord(u16);
|
|
static bool32 HasEnoughScoreForPrize(void);
|
|
static u16 GetPrizeData(void);
|
|
static void UnpackPrizeData(u16, u16 *, u16 *);
|
|
static u16 GetPrizeItemId(void);
|
|
static u16 GetPrizeQuantity(void);
|
|
static u16 GetQuantityLimitedByBag(u16, u16);
|
|
static void SpriteCB_Star(struct Sprite *);
|
|
static void SpriteCB_MonHitShake(struct Sprite *);
|
|
static void SpriteCB_MonHitFlash(struct Sprite *);
|
|
static void SpriteCB_MonIntroBounce(struct Sprite *);
|
|
static void UpdateVineSwing(int);
|
|
static void StartPokeJumpGfx(struct PokemonJumpGfx *);
|
|
static void InitPokeJumpGfx(struct PokemonJumpGfx *);
|
|
static void FreeWindowsAndDigitObj(void);
|
|
static void SetUpPokeJumpGfxFuncById(int);
|
|
static bool32 IsPokeJumpGfxFuncFinished(void);
|
|
static void SetUpResetVineGfx(void);
|
|
static bool32 ResetVineGfx(void);
|
|
static void PrintPrizeMessage(u16, u16);
|
|
static void PrintPrizeFilledBagMessage(u16);
|
|
static void PrintNoRoomForPrizeMessage(u16);
|
|
static bool32 DoPrizeMessageAndFanfare(void);
|
|
static void ClearMessageWindow(void);
|
|
static void SetMonSpriteY(u32, s16);
|
|
static void StartMonHitShake(u8);
|
|
static bool32 RemoveMessageWindow(void);
|
|
static void PrintScore(int);
|
|
static s8 HandlePlayAgainInput(void);
|
|
static int DoSameJumpTimeBonus(u8);
|
|
static void PrintJumpsInRow(u16);
|
|
static void StartMonHitFlash(u8);
|
|
static int IsMonHitShakeActive(int);
|
|
static void StopMonHitFlash(void);
|
|
static void ResetMonSpriteSubpriorities(void);
|
|
static void StartMonIntroBounce(int);
|
|
static int IsMonIntroBounceActive(void);
|
|
static void SendPacket_MonInfo(struct PokemonJump_MonInfo *);
|
|
static bool32 RecvPacket_MonInfo(int, struct PokemonJump_MonInfo *);
|
|
static void SendPacket_LeaderState(struct PokemonJump_Player *, struct PokemonJump_CommData *);
|
|
static bool32 RecvPacket_LeaderState(struct PokemonJump_Player *, struct PokemonJump_CommData *);
|
|
static void SendPacket_MemberState(struct PokemonJump_Player *, u8, u16);
|
|
static bool32 RecvPacket_MemberStateToLeader(struct PokemonJump_Player *, int, u8 *, u16 *);
|
|
static bool32 RecvPacket_MemberStateToMember(struct PokemonJump_Player *, int);
|
|
static bool32 TryUpdateRecords(u32, u16, u16);
|
|
static void IncrementGamesWithMaxPlayers(void);
|
|
static void Task_RunPokeJumpGfxFunc(u8);
|
|
static void ShowBonus(u8);
|
|
static void Task_UpdateBonus(u8);
|
|
static void LoadPokeJumpGfx(void);
|
|
static void InitDigitPrinters(void);
|
|
static void PrintScoreSuffixes(void);
|
|
static void CreateJumpMonSprites(void);
|
|
static void AddPlayerNameWindows(void);
|
|
static void DrawPlayerNameWindows(void);
|
|
static void SetUpPokeJumpGfxFunc(void (*func)(void));
|
|
static void PrintPokeJumpPlayerNames(bool32);
|
|
static u32 AddMessageWindow(u32, u32, u32, u32);
|
|
static void CreatePokeJumpYesNoMenu(u16, u16, u8);
|
|
static void PrintPlayerNamesNoHighlight(void);
|
|
static void PrintPlayerNamesWithHighlight(void);
|
|
static void ErasePlayerNames(void);
|
|
static void Msg_WantToPlayAgain(void);
|
|
static void Msg_SavingDontTurnOff(void);
|
|
static void EraseMessage(void);
|
|
static void Msg_SomeoneDroppedOut(void);
|
|
static void DoPokeJumpCountdown(void);
|
|
static void Msg_CommunicationStandby(void);
|
|
static void Task_ShowPokemonJumpRecords(u8);
|
|
static void PrintRecordsText(u16, int);
|
|
static void TruncateToFirstWordOnly(u8 *);
|
|
|
|
EWRAM_DATA static struct PokemonJump *sPokemonJump = NULL;
|
|
EWRAM_DATA static struct PokemonJumpGfx *sPokemonJumpGfx = NULL;
|
|
|
|
/*
|
|
According to the clerk, the Pokémon allowed in
|
|
Pokémon Jump are all <= 28 inches, and do not
|
|
only swim, burrow, or fly.
|
|
*/
|
|
static const struct PokemonJumpMons sPokeJumpMons[] =
|
|
{
|
|
{ .species = SPECIES_BULBASAUR, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_CHARMANDER, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_SQUIRTLE, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_CATERPIE, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_METAPOD, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_WEEDLE, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_KAKUNA, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_RATTATA, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_RATICATE, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_PIKACHU, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_SANDSHREW, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_NIDORAN_F, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_NIDORAN_M, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_CLEFAIRY, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_VULPIX, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_JIGGLYPUFF, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_ODDISH, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_PARAS, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_MEOWTH, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_PSYDUCK, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_MANKEY, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_GROWLITHE, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_POLIWAG, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_BELLSPROUT, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_SHELLDER, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_KRABBY, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_EXEGGCUTE, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_CUBONE, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_DITTO, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_EEVEE, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_OMANYTE, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_KABUTO, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_CHIKORITA, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_CYNDAQUIL, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_TOTODILE, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_SPINARAK, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_PICHU, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_CLEFFA, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_IGGLYBUFF, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_TOGEPI, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_MAREEP, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_BELLOSSOM, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_MARILL, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_SUNKERN, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_WOOPER, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_PINECO, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_SNUBBULL, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_SHUCKLE, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_TEDDIURSA, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_SLUGMA, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_SWINUB, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_HOUNDOUR, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_PHANPY, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_PORYGON2, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_TYROGUE, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_SMOOCHUM, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_ELEKID, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_MAGBY, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_LARVITAR, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_TREECKO, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_TORCHIC, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_MUDKIP, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_MARSHTOMP, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_POOCHYENA, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_ZIGZAGOON, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_LINOONE, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_WURMPLE, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_SILCOON, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_CASCOON, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_LOTAD, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_SEEDOT, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_RALTS, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_KIRLIA, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_SURSKIT, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_SHROOMISH, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_NINCADA, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_WHISMUR, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_AZURILL, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_SKITTY, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_SABLEYE, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_MAWILE, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_ARON, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_MEDITITE, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_ELECTRIKE, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_PLUSLE, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_MINUN, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_VOLBEAT, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_ILLUMISE, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_ROSELIA, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_GULPIN, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_NUMEL, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_TORKOAL, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_SPOINK, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_TRAPINCH, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_CACNEA, .jumpType = JUMP_TYPE_SLOW, },
|
|
{ .species = SPECIES_ANORITH, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_WYNAUT, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_SNORUNT, .jumpType = JUMP_TYPE_NORMAL, },
|
|
{ .species = SPECIES_CLAMPERL, .jumpType = JUMP_TYPE_FAST, },
|
|
{ .species = SPECIES_BAGON, .jumpType = JUMP_TYPE_FAST, },
|
|
};
|
|
|
|
void StartPokemonJump(u16 partyId, MainCallback exitCallback)
|
|
{
|
|
u8 taskId;
|
|
|
|
if (gReceivedRemoteLinkPlayers)
|
|
{
|
|
sPokemonJump = Alloc(sizeof(*sPokemonJump));
|
|
if (sPokemonJump)
|
|
{
|
|
ResetTasks();
|
|
taskId = CreateTask(Task_StartPokemonJump, 1);
|
|
sPokemonJump->mainState = 0;
|
|
sPokemonJump->exitCallback = exitCallback;
|
|
sPokemonJump->taskId = taskId;
|
|
sPokemonJump->multiplayerId = GetMultiplayerId();
|
|
InitJumpMonInfo(&sPokemonJump->monInfo[sPokemonJump->multiplayerId], &gPlayerParty[partyId]);
|
|
InitGame(sPokemonJump);
|
|
SetWordTaskArg(taskId, 2, (u32)sPokemonJump);
|
|
SetMainCallback2(CB2_PokemonJump);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Exit - Players not connected, or alloc failed
|
|
SetMainCallback2(exitCallback);
|
|
}
|
|
|
|
static void FreePokemonJump(void)
|
|
{
|
|
FreeWindowsAndDigitObj();
|
|
Free(sPokemonJump);
|
|
}
|
|
|
|
static void InitGame(struct PokemonJump *jump)
|
|
{
|
|
jump->numPlayers = GetLinkPlayerCount();
|
|
jump->comm.funcId = FUNC_RESET_GAME;
|
|
jump->comm.data = 0;
|
|
InitPlayerAndJumpTypes();
|
|
ResetForNewGame(jump);
|
|
if (jump->numPlayers == MAX_RFU_PLAYERS)
|
|
IncrementGamesWithMaxPlayers();
|
|
}
|
|
|
|
static void ResetForNewGame(struct PokemonJump *jump)
|
|
{
|
|
int i;
|
|
|
|
jump->vineState = VINE_UPSWING_LOWER;
|
|
jump->prevVineState = VINE_UPSWING_LOWER;
|
|
jump->vineTimer = 0;
|
|
jump->vineSpeed = 0;
|
|
jump->updateScore = FALSE;
|
|
jump->isLeader = GetMultiplayerId() == 0;
|
|
jump->mainState = 0;
|
|
jump->helperState = 0;
|
|
jump->excellentsInRow = 0;
|
|
jump->excellentsInRowRecord = 0;
|
|
jump->initScoreUpdate = FALSE;
|
|
jump->unused2 = 0;
|
|
jump->unused3 = 0;
|
|
jump->numPlayersAtPeak = 0;
|
|
jump->allowVineUpdates = FALSE;
|
|
jump->allPlayersReady = FALSE;
|
|
jump->funcActive = TRUE;
|
|
jump->comm.jumpScore = 0;
|
|
jump->comm.receivedBonusFlags = 0;
|
|
jump->comm.jumpsInRow = 0;
|
|
jump->unused4 = TRUE;
|
|
jump->showBonus = FALSE;
|
|
jump->skipJumpUpdate = FALSE;
|
|
jump->giveBonus = FALSE;
|
|
jump->linkTimer = 0;
|
|
jump->linkTimerLimit = 0;
|
|
ResetPlayersForNewGame();
|
|
ResetPlayersJumpStates();
|
|
|
|
for (i = 0; i < MAX_RFU_PLAYERS; i++)
|
|
{
|
|
jump->atJumpPeak[i] = FALSE;
|
|
jump->jumpTimeStarts[i] = 0;
|
|
}
|
|
}
|
|
|
|
static void InitPlayerAndJumpTypes(void)
|
|
{
|
|
int i, index;
|
|
|
|
for (i = 0; i < MAX_RFU_PLAYERS; i++)
|
|
{
|
|
index = GetPokemonJumpSpeciesIdx(sPokemonJump->monInfo[i].species);
|
|
sPokemonJump->players[i].monJumpType = sPokeJumpMons[index].jumpType;
|
|
}
|
|
|
|
sPokemonJump->player = &sPokemonJump->players[sPokemonJump->multiplayerId];
|
|
}
|
|
|
|
static void ResetPlayersForNewGame(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_RFU_PLAYERS; i++)
|
|
{
|
|
sPokemonJump->players[i].jumpTimeStart = 0;
|
|
sPokemonJump->players[i].monState = MONSTATE_NORMAL;
|
|
sPokemonJump->players[i].prevMonState = MONSTATE_NORMAL;
|
|
sPokemonJump->players[i].jumpOffset = 0;
|
|
sPokemonJump->players[i].jumpOffsetIdx = INT_MAX;
|
|
sPokemonJump->players[i].jumpState = JUMPSTATE_NONE;
|
|
sPokemonJump->memberFuncIds[i] = FUNC_NONE;
|
|
}
|
|
}
|
|
|
|
static s16 GetPokemonJumpSpeciesIdx(u16 species)
|
|
{
|
|
u32 i;
|
|
for (i = 0; i < ARRAY_COUNT(sPokeJumpMons); i++)
|
|
{
|
|
if (sPokeJumpMons[i].species == species)
|
|
return i;
|
|
}
|
|
|
|
return -1; // species isnt allowed
|
|
}
|
|
|
|
static void InitJumpMonInfo(struct PokemonJump_MonInfo *monInfo, struct Pokemon *mon)
|
|
{
|
|
monInfo->species = GetMonData(mon, MON_DATA_SPECIES);
|
|
monInfo->otId = GetMonData(mon, MON_DATA_OT_ID);
|
|
monInfo->personality = GetMonData(mon, MON_DATA_PERSONALITY);
|
|
}
|
|
|
|
static void VBlankCB_PokemonJump(void)
|
|
{
|
|
TransferPlttBuffer();
|
|
LoadOam();
|
|
ProcessSpriteCopyRequests();
|
|
}
|
|
|
|
static void CB2_PokemonJump(void)
|
|
{
|
|
RunTasks();
|
|
AnimateSprites();
|
|
BuildOamBuffer();
|
|
UpdatePaletteFade();
|
|
}
|
|
|
|
static void SetPokeJumpTask(TaskFunc func)
|
|
{
|
|
sPokemonJump->taskId = CreateTask(func, 1);
|
|
sPokemonJump->mainState = 0;
|
|
}
|
|
|
|
static void Task_StartPokemonJump(u8 taskId)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
SetVBlankCallback(NULL);
|
|
ResetSpriteData();
|
|
FreeAllSpritePalettes();
|
|
SetTaskWithPokeJumpStruct(Task_CommunicateMonInfo, 5);
|
|
FadeOutMapMusic(4);
|
|
sPokemonJump->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!FuncIsActiveTask(Task_CommunicateMonInfo))
|
|
{
|
|
StartPokeJumpGfx(&sPokemonJump->jumpGfx);
|
|
LoadWirelessStatusIndicatorSpriteGfx();
|
|
CreateWirelessStatusIndicatorSprite(0, 0);
|
|
sPokemonJump->mainState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!IsPokeJumpGfxFuncFinished() && IsNotWaitingForBGMStop() == TRUE)
|
|
{
|
|
FadeOutAndPlayNewMapMusic(MUS_RG_POKE_JUMP, 8);
|
|
sPokemonJump->mainState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (IsLinkTaskFinished())
|
|
{
|
|
BlendPalettes(PALETTES_ALL, 16, RGB_BLACK);
|
|
BeginNormalPaletteFade(PALETTES_ALL, -1, 16, 0, RGB_BLACK);
|
|
SetVBlankCallback(VBlankCB_PokemonJump);
|
|
sPokemonJump->mainState++;
|
|
}
|
|
break;
|
|
case 4:
|
|
UpdatePaletteFade();
|
|
if (!gPaletteFade.active)
|
|
{
|
|
sPokemonJump->startDelayTimer = 0;
|
|
sPokemonJump->mainState++;
|
|
}
|
|
break;
|
|
case 5:
|
|
sPokemonJump->startDelayTimer++;
|
|
if (sPokemonJump->startDelayTimer >= 20)
|
|
{
|
|
if (sPokemonJump->isLeader)
|
|
SetPokeJumpTask(Task_PokemonJump_Leader);
|
|
else
|
|
SetPokeJumpTask(Task_PokemonJump_Member);
|
|
|
|
InitVineState();
|
|
DestroyTask(taskId);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void SetLinkTimeInterval(int intervalId)
|
|
{
|
|
if (intervalId == LINK_INTERVAL_NONE)
|
|
{
|
|
// Link data is sent when timer reaches 0.
|
|
// Set timer to 1 and set limit to special
|
|
// 'stopped' value so timer won't change
|
|
sPokemonJump->linkTimerLimit = LINK_TIMER_STOPPED;
|
|
sPokemonJump->linkTimer = 1;
|
|
}
|
|
else
|
|
{
|
|
sPokemonJump->linkTimerLimit = (1 << (intervalId - 1)) - 1;
|
|
sPokemonJump->linkTimer = 0;
|
|
}
|
|
}
|
|
|
|
static void SetFunc_Leader(u8 funcId)
|
|
{
|
|
int i;
|
|
|
|
sPokemonJump->comm.funcId = funcId;
|
|
sPokemonJump->mainState = 0;
|
|
sPokemonJump->helperState = 0;
|
|
sPokemonJump->funcActive = TRUE;
|
|
sPokemonJump->allPlayersReady = FALSE;
|
|
for (i = 1; i < sPokemonJump->numPlayers; i++)
|
|
sPokemonJump->players[i].funcFinished = FALSE;
|
|
}
|
|
|
|
static void RecvLinkData_Leader(void)
|
|
{
|
|
int i;
|
|
int numReady;
|
|
u16 monState;
|
|
u8 funcId;
|
|
u16 playAgainState;
|
|
|
|
for (i = 1, numReady = 0; i < sPokemonJump->numPlayers; i++)
|
|
{
|
|
monState = sPokemonJump->players[i].monState;
|
|
if (RecvPacket_MemberStateToLeader(&sPokemonJump->players[i], i, &funcId, &playAgainState))
|
|
{
|
|
sPokemonJump->playAgainStates[i] = playAgainState;
|
|
sPokemonJump->memberFuncIds[i] = funcId;
|
|
sPokemonJump->players[i].prevMonState = monState;
|
|
}
|
|
|
|
// Group member has finished currently assigned function
|
|
if (sPokemonJump->players[i].funcFinished && sPokemonJump->memberFuncIds[i] == sPokemonJump->comm.funcId)
|
|
numReady++;
|
|
}
|
|
|
|
if (numReady == sPokemonJump->numPlayers - 1)
|
|
sPokemonJump->allPlayersReady = TRUE;
|
|
}
|
|
|
|
static bool32 (* const sPokeJumpLeaderFuncs[])(void) =
|
|
{
|
|
[FUNC_GAME_INTRO] = GameIntro_Leader,
|
|
[FUNC_WAIT_ROUND] = WaitRound_Leader,
|
|
[FUNC_GAME_ROUND] = GameRound_Leader,
|
|
[FUNC_GAME_OVER] = GameOver_Leader,
|
|
[FUNC_ASK_PLAY_AGAIN] = AskPlayAgain_Leader,
|
|
[FUNC_RESET_GAME] = ResetGame_Leader,
|
|
[FUNC_EXIT] = ExitGame,
|
|
[FUNC_GIVE_PRIZE] = GivePrize_Leader,
|
|
[FUNC_SAVE] = SavePokeJump,
|
|
};
|
|
|
|
static void Task_PokemonJump_Leader(u8 taskId)
|
|
{
|
|
RecvLinkData_Leader();
|
|
TryUpdateScore();
|
|
if (!sPokemonJump->funcActive && sPokemonJump->allPlayersReady)
|
|
{
|
|
SetFunc_Leader(sPokemonJump->nextFuncId);
|
|
SetLinkTimeInterval(LINK_INTERVAL_SHORT);
|
|
}
|
|
|
|
if (sPokemonJump->funcActive == TRUE)
|
|
{
|
|
if (!sPokeJumpLeaderFuncs[sPokemonJump->comm.funcId]())
|
|
{
|
|
sPokemonJump->funcActive = FALSE;
|
|
sPokemonJump->players[sPokemonJump->multiplayerId].funcFinished = TRUE;
|
|
}
|
|
}
|
|
|
|
UpdateGame();
|
|
SendLinkData_Leader();
|
|
}
|
|
|
|
static void SendLinkData_Leader(void)
|
|
{
|
|
if (!sPokemonJump->linkTimer)
|
|
SendPacket_LeaderState(sPokemonJump->players, &sPokemonJump->comm);
|
|
|
|
if (sPokemonJump->linkTimerLimit != LINK_TIMER_STOPPED)
|
|
{
|
|
sPokemonJump->linkTimer++;
|
|
sPokemonJump->linkTimer &= sPokemonJump->linkTimerLimit;
|
|
}
|
|
}
|
|
|
|
static void SetFunc_Member(u8 funcId)
|
|
{
|
|
sPokemonJump->comm.funcId = funcId;
|
|
sPokemonJump->mainState = 0;
|
|
sPokemonJump->helperState = 0;
|
|
sPokemonJump->funcActive = TRUE;
|
|
sPokemonJump->players[sPokemonJump->multiplayerId].funcFinished = FALSE;
|
|
}
|
|
|
|
static void RecvLinkData_Member(void)
|
|
{
|
|
int i;
|
|
u16 monState;
|
|
struct PokemonJump_CommData leaderData;
|
|
|
|
monState = sPokemonJump->players[0].monState;
|
|
if (RecvPacket_LeaderState(sPokemonJump->players, &leaderData))
|
|
{
|
|
if (sPokemonJump->players[sPokemonJump->multiplayerId].funcFinished == TRUE
|
|
&& leaderData.funcId != sPokemonJump->comm.funcId)
|
|
{
|
|
SetFunc_Member(leaderData.funcId);
|
|
}
|
|
|
|
if (sPokemonJump->comm.jumpScore != leaderData.jumpScore)
|
|
{
|
|
sPokemonJump->comm.jumpScore = leaderData.jumpScore;
|
|
sPokemonJump->updateScore = TRUE;
|
|
sPokemonJump->comm.receivedBonusFlags = leaderData.receivedBonusFlags;
|
|
if (sPokemonJump->comm.receivedBonusFlags)
|
|
sPokemonJump->showBonus = TRUE;
|
|
else
|
|
sPokemonJump->showBonus = FALSE;
|
|
}
|
|
|
|
sPokemonJump->comm.data = leaderData.data;
|
|
sPokemonJump->comm.jumpsInRow = leaderData.jumpsInRow;
|
|
sPokemonJump->players[0].prevMonState = monState;
|
|
}
|
|
|
|
for (i = 1; i < sPokemonJump->numPlayers; i++)
|
|
{
|
|
if (i != sPokemonJump->multiplayerId)
|
|
{
|
|
monState = sPokemonJump->players[i].monState;
|
|
if (RecvPacket_MemberStateToMember(&sPokemonJump->players[i], i))
|
|
sPokemonJump->players[i].prevMonState = monState;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool32 (* const sPokeJumpMemberFuncs[])(void) =
|
|
{
|
|
[FUNC_GAME_INTRO] = GameIntro_Member,
|
|
[FUNC_WAIT_ROUND] = WaitRound_Member,
|
|
[FUNC_GAME_ROUND] = GameRound_Member,
|
|
[FUNC_GAME_OVER] = GameOver_Member,
|
|
[FUNC_ASK_PLAY_AGAIN] = AskPlayAgain_Member,
|
|
[FUNC_RESET_GAME] = ResetGame_Member,
|
|
[FUNC_EXIT] = ExitGame,
|
|
[FUNC_GIVE_PRIZE] = GivePrize_Member,
|
|
[FUNC_SAVE] = SavePokeJump,
|
|
};
|
|
|
|
static void Task_PokemonJump_Member(u8 taskId)
|
|
{
|
|
RecvLinkData_Member();
|
|
if (sPokemonJump->funcActive)
|
|
{
|
|
if (!sPokeJumpMemberFuncs[sPokemonJump->comm.funcId]())
|
|
{
|
|
sPokemonJump->funcActive = FALSE;
|
|
sPokemonJump->players[sPokemonJump->multiplayerId].funcFinished = TRUE;
|
|
SetLinkTimeInterval(LINK_INTERVAL_SHORT);
|
|
}
|
|
}
|
|
|
|
UpdateGame();
|
|
SendLinkData_Member();
|
|
}
|
|
|
|
static void SendLinkData_Member(void)
|
|
{
|
|
if (!sPokemonJump->linkTimer)
|
|
SendPacket_MemberState(&sPokemonJump->players[sPokemonJump->multiplayerId], sPokemonJump->comm.funcId, sPokemonJump->playAgainComm);
|
|
|
|
if (sPokemonJump->linkTimerLimit != LINK_TIMER_STOPPED)
|
|
{
|
|
sPokemonJump->linkTimer++;
|
|
sPokemonJump->linkTimer &= sPokemonJump->linkTimerLimit;
|
|
}
|
|
}
|
|
|
|
static bool32 GameIntro_Leader(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
SetLinkTimeInterval(LINK_INTERVAL_SHORT);
|
|
sPokemonJump->mainState++;
|
|
// fall through
|
|
case 1:
|
|
if (!DoGameIntro())
|
|
{
|
|
sPokemonJump->comm.data = sPokemonJump->vineTimer;
|
|
sPokemonJump->nextFuncId = FUNC_WAIT_ROUND;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 GameIntro_Member(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
SetLinkTimeInterval(LINK_INTERVAL_NONE);
|
|
sPokemonJump->rngSeed = sPokemonJump->comm.data;
|
|
sPokemonJump->mainState++;
|
|
// fall through
|
|
case 1:
|
|
return DoGameIntro();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 WaitRound_Leader(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
ResetPlayersJumpStates();
|
|
SetLinkTimeInterval(LINK_INTERVAL_LONG);
|
|
sPokemonJump->mainState++;
|
|
break;
|
|
case 1:
|
|
if (sPokemonJump->allPlayersReady)
|
|
{
|
|
sPokemonJump->nextFuncId = FUNC_GAME_ROUND;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 WaitRound_Member(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
ResetPlayersJumpStates();
|
|
SetLinkTimeInterval(LINK_INTERVAL_NONE);
|
|
sPokemonJump->vineTimer = sPokemonJump->comm.data;
|
|
sPokemonJump->mainState++;
|
|
// fall through
|
|
case 1:
|
|
if (AreLinkQueuesEmpty())
|
|
return FALSE;
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 GameRound_Leader(void)
|
|
{
|
|
if (!HandleSwingRound())
|
|
{
|
|
sPokemonJump->comm.data = sPokemonJump->vineTimer;
|
|
sPokemonJump->nextFuncId = FUNC_WAIT_ROUND;
|
|
}
|
|
else if (UpdateVineHitStates())
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Someone hit the vine
|
|
ResetVineAfterHit();
|
|
sPokemonJump->nextFuncId = FUNC_GAME_OVER;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static bool32 GameRound_Member(void)
|
|
{
|
|
if (!HandleSwingRound())
|
|
;
|
|
else if (UpdateVineHitStates())
|
|
return TRUE;
|
|
else // Someone hit the vine
|
|
ResetVineAfterHit();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static bool32 GameOver_Leader(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
UpdateVineHitStates();
|
|
if (AllPlayersJumpedOrHit())
|
|
sPokemonJump->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!DoVineHitEffect())
|
|
{
|
|
if (HasEnoughScoreForPrize())
|
|
{
|
|
sPokemonJump->comm.data = GetPrizeData();
|
|
sPokemonJump->nextFuncId = FUNC_GIVE_PRIZE;
|
|
}
|
|
else if (sPokemonJump->comm.jumpsInRow >= 200)
|
|
{
|
|
sPokemonJump->comm.data = sPokemonJump->excellentsInRowRecord;
|
|
sPokemonJump->nextFuncId = FUNC_SAVE;
|
|
}
|
|
else
|
|
{
|
|
sPokemonJump->comm.data = sPokemonJump->excellentsInRowRecord;
|
|
sPokemonJump->nextFuncId = FUNC_ASK_PLAY_AGAIN;
|
|
}
|
|
|
|
sPokemonJump->mainState++;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case 2:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 GameOver_Member(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
if (!UpdateVineHitStates())
|
|
ResetVineAfterHit();
|
|
if (AllPlayersJumpedOrHit())
|
|
sPokemonJump->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!DoVineHitEffect())
|
|
{
|
|
sPokemonJump->mainState++;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case 2:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 AskPlayAgain_Leader(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
SetLinkTimeInterval(LINK_INTERVAL_MEDIUM);
|
|
sPokemonJump->mainState++;
|
|
// fall through
|
|
case 1:
|
|
if (!DoPlayAgainPrompt())
|
|
{
|
|
TryUpdateRecords(sPokemonJump->comm.jumpScore, sPokemonJump->comm.jumpsInRow, sPokemonJump->comm.data);
|
|
sPokemonJump->mainState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (sPokemonJump->allPlayersReady)
|
|
{
|
|
if (ShouldPlayAgain())
|
|
sPokemonJump->nextFuncId = FUNC_RESET_GAME;
|
|
else
|
|
sPokemonJump->nextFuncId = FUNC_EXIT;
|
|
|
|
sPokemonJump->mainState++;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case 3:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 AskPlayAgain_Member(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
SetLinkTimeInterval(LINK_INTERVAL_NONE);
|
|
sPokemonJump->mainState++;
|
|
// fall through
|
|
case 1:
|
|
if (!DoPlayAgainPrompt())
|
|
{
|
|
TryUpdateRecords(sPokemonJump->comm.jumpScore, sPokemonJump->comm.jumpsInRow, sPokemonJump->comm.data);
|
|
sPokemonJump->playAgainComm = sPokemonJump->playAgainState;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 ResetGame_Leader(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
if (!CloseMessageAndResetScore())
|
|
sPokemonJump->mainState++;
|
|
break;
|
|
case 1:
|
|
if (sPokemonJump->allPlayersReady)
|
|
{
|
|
ResetForNewGame(sPokemonJump);
|
|
sPokemonJump->rngSeed = Random();
|
|
sPokemonJump->comm.data = sPokemonJump->rngSeed;
|
|
sPokemonJump->nextFuncId = FUNC_GAME_INTRO;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 ResetGame_Member(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
if (!CloseMessageAndResetScore())
|
|
{
|
|
ResetForNewGame(sPokemonJump);
|
|
sPokemonJump->mainState++;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case 1:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 ExitGame(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
sPokemonJump->mainState = 1;
|
|
break;
|
|
case 1:
|
|
SetLinkTimeInterval(LINK_INTERVAL_NONE);
|
|
sPokemonJump->mainState++;
|
|
break;
|
|
case 2:
|
|
if (!ClosePokeJumpLink())
|
|
{
|
|
SetMainCallback2(sPokemonJump->exitCallback);
|
|
FreePokemonJump();
|
|
}
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 GivePrize_Leader(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
SetLinkTimeInterval(LINK_INTERVAL_MEDIUM);
|
|
sPokemonJump->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!TryGivePrize())
|
|
{
|
|
sPokemonJump->comm.data = sPokemonJump->excellentsInRowRecord;
|
|
sPokemonJump->nextFuncId = FUNC_SAVE;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 GivePrize_Member(void)
|
|
{
|
|
SetLinkTimeInterval(LINK_INTERVAL_NONE);
|
|
if (!TryGivePrize())
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 SavePokeJump(void)
|
|
{
|
|
switch (sPokemonJump->mainState)
|
|
{
|
|
case 0:
|
|
TryUpdateRecords(sPokemonJump->comm.jumpScore, sPokemonJump->comm.jumpsInRow, sPokemonJump->comm.data);
|
|
SetUpPokeJumpGfxFuncById(GFXFUNC_MSG_SAVING);
|
|
sPokemonJump->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!IsPokeJumpGfxFuncFinished())
|
|
{
|
|
SetLinkTimeInterval(LINK_INTERVAL_NONE);
|
|
sPokemonJump->mainState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (AreLinkQueuesEmpty())
|
|
{
|
|
CreateTask(Task_LinkSave, 6);
|
|
sPokemonJump->mainState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (!FuncIsActiveTask(Task_LinkSave))
|
|
{
|
|
ClearMessageWindow();
|
|
sPokemonJump->mainState++;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (!RemoveMessageWindow())
|
|
{
|
|
sPokemonJump->nextFuncId = FUNC_ASK_PLAY_AGAIN;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 DoGameIntro(void)
|
|
{
|
|
switch (sPokemonJump->helperState)
|
|
{
|
|
case 0:
|
|
SetUpPokeJumpGfxFuncById(GFXFUNC_SHOW_NAMES_HIGHLIGHT);
|
|
ResetMonSpriteSubpriorities();
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 1:
|
|
if (!IsPokeJumpGfxFuncFinished())
|
|
{
|
|
StartMonIntroBounce(sPokemonJump->multiplayerId);
|
|
sPokemonJump->timer = 0;
|
|
sPokemonJump->helperState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (++sPokemonJump->timer > 120)
|
|
{
|
|
SetUpPokeJumpGfxFuncById(GFXFUNC_ERASE_NAMES);
|
|
sPokemonJump->helperState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (IsPokeJumpGfxFuncFinished() != TRUE && IsMonIntroBounceActive() != TRUE)
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 4:
|
|
SetUpPokeJumpGfxFuncById(GFXFUNC_COUNTDOWN);
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 5:
|
|
if (!IsPokeJumpGfxFuncFinished())
|
|
{
|
|
DisallowVineUpdates();
|
|
SetUpResetVineGfx();
|
|
sPokemonJump->helperState++;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (!ResetVineGfx())
|
|
{
|
|
AllowVineUpdates();
|
|
ResetVineState();
|
|
sPokemonJump->helperState++;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case 7:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Update the vine and wait for player to input a jump
|
|
// Returns false when vine reaches the 'hit' point, after
|
|
// which input is ignored
|
|
static bool32 HandleSwingRound(void)
|
|
{
|
|
UpdateVineState();
|
|
if (sPokemonJump->ignoreJumpInput)
|
|
{
|
|
sPokemonJump->ignoreJumpInput = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
switch (sPokemonJump->helperState)
|
|
{
|
|
case 0:
|
|
if (IsPlayersMonState(MONSTATE_NORMAL))
|
|
sPokemonJump->helperState++;
|
|
else
|
|
break;
|
|
// fall through
|
|
case 1:
|
|
if (JOY_NEW(A_BUTTON))
|
|
{
|
|
SetMonStateJump();
|
|
SetLinkTimeInterval(LINK_INTERVAL_SHORT);
|
|
sPokemonJump->helperState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (IsPlayersMonState(MONSTATE_JUMP) == TRUE)
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 3:
|
|
if (IsPlayersMonState(MONSTATE_NORMAL) == TRUE)
|
|
sPokemonJump->helperState = 0;
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 DoVineHitEffect(void)
|
|
{
|
|
int i;
|
|
|
|
switch (sPokemonJump->helperState)
|
|
{
|
|
case 0:
|
|
for (i = 0; i < sPokemonJump->numPlayers; i++)
|
|
{
|
|
if (IsMonHitShakeActive(i) == TRUE)
|
|
return TRUE;
|
|
}
|
|
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 1:
|
|
for (i = 0; i < sPokemonJump->numPlayers; i++)
|
|
{
|
|
if (sPokemonJump->players[i].monState == MONSTATE_HIT)
|
|
StartMonHitFlash(i);
|
|
}
|
|
|
|
SetUpPokeJumpGfxFuncById(GFXFUNC_SHOW_NAMES);
|
|
sPokemonJump->timer = 0;
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 2:
|
|
if (++sPokemonJump->timer > 100)
|
|
{
|
|
SetUpPokeJumpGfxFuncById(GFXFUNC_ERASE_NAMES);
|
|
sPokemonJump->timer = 0;
|
|
sPokemonJump->helperState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (!IsPokeJumpGfxFuncFinished())
|
|
{
|
|
StopMonHitFlash();
|
|
sPokemonJump->comm.receivedBonusFlags = 0;
|
|
ResetPlayersMonState();
|
|
sPokemonJump->helperState++;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case 4:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 TryGivePrize(void)
|
|
{
|
|
switch (sPokemonJump->helperState)
|
|
{
|
|
case 0:
|
|
UnpackPrizeData(sPokemonJump->comm.data, &sPokemonJump->prizeItemId, &sPokemonJump->prizeItemQuantity);
|
|
PrintPrizeMessage(sPokemonJump->prizeItemId, sPokemonJump->prizeItemQuantity);
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 1:
|
|
case 4:
|
|
if (!DoPrizeMessageAndFanfare())
|
|
{
|
|
sPokemonJump->timer = 0;
|
|
sPokemonJump->helperState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
case 5:
|
|
// Wait to continue after message
|
|
sPokemonJump->timer++;
|
|
if (JOY_NEW(A_BUTTON | B_BUTTON) || sPokemonJump->timer > 180)
|
|
{
|
|
ClearMessageWindow();
|
|
sPokemonJump->helperState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (!RemoveMessageWindow())
|
|
{
|
|
sPokemonJump->prizeItemQuantity = GetQuantityLimitedByBag(sPokemonJump->prizeItemId, sPokemonJump->prizeItemQuantity);
|
|
if (sPokemonJump->prizeItemQuantity && AddBagItem(sPokemonJump->prizeItemId, sPokemonJump->prizeItemQuantity))
|
|
{
|
|
if (!CheckBagHasSpace(sPokemonJump->prizeItemId, 1))
|
|
{
|
|
// An item was given successfully, but no room for any more.
|
|
// It's possible the full prize quantity had to be limited
|
|
PrintPrizeFilledBagMessage(sPokemonJump->prizeItemId);
|
|
sPokemonJump->helperState = 4; // Do message
|
|
}
|
|
else
|
|
{
|
|
sPokemonJump->helperState = 6; // Exit
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PrintNoRoomForPrizeMessage(sPokemonJump->prizeItemId);
|
|
sPokemonJump->helperState = 4; // Do message
|
|
}
|
|
}
|
|
break;
|
|
case 6:
|
|
if (!RemoveMessageWindow())
|
|
return FALSE;
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 DoPlayAgainPrompt(void)
|
|
{
|
|
s8 input;
|
|
|
|
switch (sPokemonJump->helperState)
|
|
{
|
|
case 0:
|
|
SetUpPokeJumpGfxFuncById(GFXFUNC_MSG_PLAY_AGAIN);
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 1:
|
|
if (!IsPokeJumpGfxFuncFinished())
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 2:
|
|
input = HandlePlayAgainInput();
|
|
switch (input)
|
|
{
|
|
case MENU_B_PRESSED:
|
|
case 1: // No
|
|
sPokemonJump->playAgainState = PLAY_AGAIN_NO;
|
|
SetUpPokeJumpGfxFuncById(GFXFUNC_ERASE_MSG);
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 0: // Yes
|
|
sPokemonJump->playAgainState = PLAY_AGAIN_YES;
|
|
SetUpPokeJumpGfxFuncById(GFXFUNC_ERASE_MSG);
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (!IsPokeJumpGfxFuncFinished())
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 4:
|
|
SetUpPokeJumpGfxFuncById(GFXFUNC_MSG_COMM_STANDBY);
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 5:
|
|
if (!IsPokeJumpGfxFuncFinished())
|
|
{
|
|
sPokemonJump->helperState++;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case 6:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 ClosePokeJumpLink(void)
|
|
{
|
|
switch (sPokemonJump->helperState)
|
|
{
|
|
case 0:
|
|
ClearMessageWindow();
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 1:
|
|
if (!RemoveMessageWindow())
|
|
{
|
|
SetUpPokeJumpGfxFuncById(GFXFUNC_MSG_PLAYER_DROPPED);
|
|
sPokemonJump->helperState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!IsPokeJumpGfxFuncFinished())
|
|
{
|
|
sPokemonJump->timer = 0;
|
|
sPokemonJump->helperState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (++sPokemonJump->timer > 120)
|
|
{
|
|
BeginNormalPaletteFade(PALETTES_ALL, -1, 0, 16, RGB_BLACK);
|
|
sPokemonJump->helperState++;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (!gPaletteFade.active)
|
|
{
|
|
SetCloseLinkCallback();
|
|
sPokemonJump->helperState++;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (!gReceivedRemoteLinkPlayers)
|
|
return FALSE;
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 CloseMessageAndResetScore(void)
|
|
{
|
|
switch (sPokemonJump->helperState)
|
|
{
|
|
case 0:
|
|
ClearMessageWindow();
|
|
PrintScore(0);
|
|
sPokemonJump->helperState++;
|
|
break;
|
|
case 1:
|
|
if (!RemoveMessageWindow())
|
|
{
|
|
sPokemonJump->helperState++;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case 2:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define tState data[0]
|
|
#define tNumReceived data[1]
|
|
#define tReceivedPacket(playerId) data[(playerId) + 2]
|
|
#define DATAIDX_GAME_STRUCT 14
|
|
|
|
static void Task_CommunicateMonInfo(u8 taskId)
|
|
{
|
|
int i;
|
|
s16 *data = gTasks[taskId].data;
|
|
struct PokemonJump *jump = (struct PokemonJump *)GetWordTaskArg(taskId, DATAIDX_GAME_STRUCT);
|
|
|
|
switch (tState)
|
|
{
|
|
case 0:
|
|
for (i = 0; i < MAX_RFU_PLAYERS; i++)
|
|
tReceivedPacket(i) = FALSE;
|
|
|
|
tState++;
|
|
// fall through
|
|
case 1:
|
|
SendPacket_MonInfo(&jump->monInfo[jump->multiplayerId]);
|
|
for (i = 0; i < MAX_RFU_PLAYERS; i++)
|
|
{
|
|
if (!tReceivedPacket(i) && RecvPacket_MonInfo(i, &jump->monInfo[i]))
|
|
{
|
|
StringCopy(jump->players[i].name, gLinkPlayers[i].name);
|
|
tReceivedPacket(i) = TRUE;
|
|
tNumReceived++;
|
|
if (tNumReceived == jump->numPlayers)
|
|
{
|
|
InitPlayerAndJumpTypes();
|
|
DestroyTask(taskId);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void SetTaskWithPokeJumpStruct(TaskFunc func, u8 taskPriority)
|
|
{
|
|
u8 taskId = CreateTask(func, taskPriority);
|
|
SetWordTaskArg(taskId, DATAIDX_GAME_STRUCT, (u32)sPokemonJump);
|
|
}
|
|
|
|
#undef tState
|
|
#undef tNumReceived
|
|
#undef tReceivedPacket
|
|
#undef DATAIDX_GAME_STRUCT
|
|
|
|
static void InitVineState(void)
|
|
{
|
|
sPokemonJump->vineTimer = 0;
|
|
sPokemonJump->vineState = VINE_UPSWING_LOWER;
|
|
sPokemonJump->vineStateTimer = 0;
|
|
sPokemonJump->vineSpeed = 0;
|
|
sPokemonJump->ignoreJumpInput = FALSE;
|
|
sPokemonJump->gameOver = FALSE;
|
|
}
|
|
|
|
static void ResetVineState(void)
|
|
{
|
|
sPokemonJump->vineTimer = 0;
|
|
sPokemonJump->vineStateTimer = VINE_STATE_TIMER(VINE_UPSWING_LOWER);
|
|
sPokemonJump->vineState = VINE_UPSWING_LOW;
|
|
sPokemonJump->ignoreJumpInput = FALSE;
|
|
sPokemonJump->gameOver = FALSE;
|
|
sPokemonJump->vineSpeedStage = 0;
|
|
sPokemonJump->vineBaseSpeedIdx = 0;
|
|
sPokemonJump->vineSpeedAccel = 0;
|
|
sPokemonJump->vineSpeedDelay = 0;
|
|
sPokemonJump->atMaxSpeedStage = FALSE;
|
|
UpdateVineSpeed();
|
|
}
|
|
|
|
static void UpdateVineState(void)
|
|
{
|
|
if (sPokemonJump->allowVineUpdates)
|
|
{
|
|
sPokemonJump->vineTimer++;
|
|
sPokemonJump->vineStateTimer += GetVineSpeed();
|
|
if (sPokemonJump->vineStateTimer >= VINE_STATE_TIMER(NUM_VINESTATES - 1))
|
|
sPokemonJump->vineStateTimer -= VINE_STATE_TIMER(NUM_VINESTATES - 1);
|
|
|
|
sPokemonJump->prevVineState = sPokemonJump->vineState;
|
|
sPokemonJump->vineState = sPokemonJump->vineStateTimer >> 8;
|
|
|
|
// If beginning upswing
|
|
if (sPokemonJump->vineState > VINE_UPSWING_LOWER && sPokemonJump->prevVineState < VINE_UPSWING_LOW)
|
|
{
|
|
sPokemonJump->ignoreJumpInput++;
|
|
UpdateVineSpeed();
|
|
}
|
|
}
|
|
}
|
|
|
|
static int GetVineSpeed(void)
|
|
{
|
|
int speed;
|
|
|
|
if (sPokemonJump->gameOver)
|
|
return 0;
|
|
|
|
speed = sPokemonJump->vineSpeed;
|
|
if (sPokemonJump->vineStateTimer <= VINE_STATE_TIMER(VINE_LOWEST))
|
|
{
|
|
// If at or below lowest, then vine is in downswing
|
|
// Increase speed in downswing
|
|
sPokemonJump->vineSpeedAccel += 80;
|
|
speed += sPokemonJump->vineSpeedAccel / 256;
|
|
}
|
|
|
|
return speed;
|
|
}
|
|
|
|
static const u16 sVineBaseSpeeds[] = {26, 31, 36, 41, 46, 51, 56, 61};
|
|
static const u16 sVineSpeedDelays[] = {0, 1, 1, 2};
|
|
|
|
static void UpdateVineSpeed(void)
|
|
{
|
|
int baseSpeed;
|
|
|
|
sPokemonJump->vineSpeedAccel = 0;
|
|
if (sPokemonJump->vineSpeedDelay)
|
|
{
|
|
sPokemonJump->vineSpeedDelay--;
|
|
if (sPokemonJump->atMaxSpeedStage)
|
|
{
|
|
if (PokeJumpRandom() % 4)
|
|
{
|
|
sPokemonJump->vineSpeed = sPokemonJump->nextVineSpeed;
|
|
}
|
|
else
|
|
{
|
|
if (sPokemonJump->nextVineSpeed > 54)
|
|
sPokemonJump->vineSpeed = 30;
|
|
else
|
|
sPokemonJump->vineSpeed = 82;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(sPokemonJump->vineBaseSpeedIdx & ARRAY_COUNT(sVineBaseSpeeds)))
|
|
{
|
|
sPokemonJump->nextVineSpeed = sVineBaseSpeeds[sPokemonJump->vineBaseSpeedIdx] + (sPokemonJump->vineSpeedStage * 7);
|
|
sPokemonJump->vineSpeedDelay = sVineSpeedDelays[PokeJumpRandom() % ARRAY_COUNT(sVineSpeedDelays)] + 2;
|
|
sPokemonJump->vineBaseSpeedIdx++;
|
|
}
|
|
else
|
|
{
|
|
if (sPokemonJump->vineBaseSpeedIdx == ARRAY_COUNT(sVineBaseSpeeds))
|
|
{
|
|
if (sPokemonJump->vineSpeedStage < 3)
|
|
sPokemonJump->vineSpeedStage++;
|
|
else
|
|
sPokemonJump->atMaxSpeedStage = TRUE;
|
|
}
|
|
|
|
baseSpeed = sVineBaseSpeeds[15 - sPokemonJump->vineBaseSpeedIdx];
|
|
sPokemonJump->nextVineSpeed = baseSpeed + (sPokemonJump->vineSpeedStage * 7);
|
|
if (++sPokemonJump->vineBaseSpeedIdx > 15)
|
|
{
|
|
if (PokeJumpRandom() % 4 == 0)
|
|
sPokemonJump->nextVineSpeed -= 5;
|
|
|
|
sPokemonJump->vineBaseSpeedIdx = 0;
|
|
}
|
|
}
|
|
|
|
sPokemonJump->vineSpeed = sPokemonJump->nextVineSpeed;
|
|
}
|
|
}
|
|
|
|
static int PokeJumpRandom(void)
|
|
{
|
|
sPokemonJump->rngSeed = ISO_RANDOMIZE1(sPokemonJump->rngSeed);
|
|
return sPokemonJump->rngSeed >> 16;
|
|
}
|
|
|
|
static void ResetVineAfterHit(void)
|
|
{
|
|
sPokemonJump->gameOver = TRUE;
|
|
sPokemonJump->vineState = VINE_UPSWING_LOWER;
|
|
sPokemonJump->vineStateTimer = VINE_STATE_TIMER(VINE_LOWEST);
|
|
AllowVineUpdates();
|
|
}
|
|
|
|
static int IsGameOver(void)
|
|
{
|
|
return sPokemonJump->gameOver;
|
|
}
|
|
|
|
static void ResetPlayersJumpStates(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAX_RFU_PLAYERS; i++)
|
|
sPokemonJump->players[i].jumpState = JUMPSTATE_NONE;
|
|
}
|
|
|
|
static void ResetPlayersMonState(void)
|
|
{
|
|
sPokemonJump->player->monState = MONSTATE_NORMAL;
|
|
sPokemonJump->player->prevMonState = MONSTATE_NORMAL;
|
|
}
|
|
|
|
static bool32 IsPlayersMonState(u16 monState)
|
|
{
|
|
if (sPokemonJump->players[sPokemonJump->multiplayerId].monState == monState)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
static void SetMonStateJump(void)
|
|
{
|
|
sPokemonJump->player->jumpTimeStart = sPokemonJump->vineTimer;
|
|
sPokemonJump->player->prevMonState = sPokemonJump->player->monState;
|
|
sPokemonJump->player->monState = MONSTATE_JUMP;
|
|
}
|
|
|
|
static void SetMonStateHit(void)
|
|
{
|
|
sPokemonJump->player->prevMonState = sPokemonJump->player->monState;
|
|
sPokemonJump->player->monState = MONSTATE_HIT;
|
|
sPokemonJump->player->jumpTimeStart = sPokemonJump->vineTimer;
|
|
sPokemonJump->player->jumpState = JUMPSTATE_FAILURE;
|
|
}
|
|
|
|
static void SetMonStateNormal(void)
|
|
{
|
|
sPokemonJump->player->prevMonState = sPokemonJump->player->monState;
|
|
sPokemonJump->player->monState = MONSTATE_NORMAL;
|
|
}
|
|
|
|
static const u16 sSoundEffects[MAX_RFU_PLAYERS - 1] = {SE_SHOP, SE_SHINY, SE_M_MORNING_SUN, SE_RG_POKE_JUMP_SUCCESS};
|
|
|
|
static void UpdateGame(void)
|
|
{
|
|
if (sPokemonJump->updateScore)
|
|
{
|
|
PrintScore(sPokemonJump->comm.jumpScore);
|
|
sPokemonJump->updateScore = FALSE;
|
|
if (sPokemonJump->showBonus)
|
|
{
|
|
int numPlayers = DoSameJumpTimeBonus(sPokemonJump->comm.receivedBonusFlags);
|
|
PlaySE(sSoundEffects[numPlayers - 2]);
|
|
sPokemonJump->showBonus = FALSE;
|
|
}
|
|
}
|
|
|
|
PrintJumpsInRow(sPokemonJump->comm.jumpsInRow);
|
|
HandleMonState();
|
|
TryUpdateVineSwing();
|
|
}
|
|
|
|
static void TryUpdateVineSwing(void)
|
|
{
|
|
if (sPokemonJump->allowVineUpdates)
|
|
UpdateVineSwing(sPokemonJump->vineState);
|
|
}
|
|
|
|
static void DisallowVineUpdates(void)
|
|
{
|
|
sPokemonJump->allowVineUpdates = FALSE;
|
|
}
|
|
|
|
static void AllowVineUpdates(void)
|
|
{
|
|
sPokemonJump->allowVineUpdates = TRUE;
|
|
}
|
|
|
|
#define F_SE_JUMP (1 << 0)
|
|
#define F_SE_FAIL (1 << 1)
|
|
|
|
static void HandleMonState(void)
|
|
{
|
|
int i;
|
|
int soundFlags = 0;
|
|
int numPlayers = sPokemonJump->numPlayers;
|
|
|
|
for (i = 0; i < numPlayers; i++)
|
|
{
|
|
switch (sPokemonJump->players[i].monState)
|
|
{
|
|
case MONSTATE_NORMAL:
|
|
SetMonSpriteY(i, 0);
|
|
break;
|
|
case MONSTATE_JUMP:
|
|
if (sPokemonJump->players[i].prevMonState != MONSTATE_JUMP || sPokemonJump->players[i].jumpTimeStart != sPokemonJump->jumpTimeStarts[i])
|
|
{
|
|
// This is a new jump, play SE and init fields for jump handling
|
|
if (i == sPokemonJump->multiplayerId)
|
|
sPokemonJump->players[i].prevMonState = MONSTATE_JUMP;
|
|
|
|
soundFlags |= F_SE_JUMP;
|
|
sPokemonJump->players[i].jumpOffsetIdx = INT_MAX;
|
|
sPokemonJump->jumpTimeStarts[i] = sPokemonJump->players[i].jumpTimeStart;
|
|
}
|
|
|
|
UpdateJump(i);
|
|
break;
|
|
case MONSTATE_HIT:
|
|
if (sPokemonJump->players[i].prevMonState != MONSTATE_HIT)
|
|
{
|
|
if (i == sPokemonJump->multiplayerId)
|
|
sPokemonJump->players[i].prevMonState = MONSTATE_HIT;
|
|
|
|
soundFlags |= F_SE_FAIL;
|
|
StartMonHitShake(i);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (soundFlags & F_SE_FAIL)
|
|
PlaySE(SE_RG_POKE_JUMP_FAILURE);
|
|
else if (soundFlags & F_SE_JUMP)
|
|
PlaySE(SE_LEDGE);
|
|
}
|
|
|
|
static const s8 sJumpOffsets[][48] =
|
|
{
|
|
[JUMP_TYPE_NORMAL] = { -3, -6, -8, -10, -13, -15, -17, -19,
|
|
-21, -23, -25, -27, -28, -29,
|
|
JUMP_PEAK, JUMP_PEAK, JUMP_PEAK,
|
|
-28, -27, -26, -25, -23, -22, -20, -18,
|
|
-17, -15, -13, -11, -8, -6, -4, -1},
|
|
|
|
[JUMP_TYPE_FAST] = { -3, -6, -9, -11, -14, -16, -18, -20,
|
|
-22, -24, -26, -28, -29,
|
|
JUMP_PEAK, JUMP_PEAK,
|
|
-28, -26, -24, -22, -20, -18, -16, -14,
|
|
-11, -9, -6, -4, -1},
|
|
|
|
[JUMP_TYPE_SLOW] = { -3, -6, -9, -11, -13, -15, -17, -19,
|
|
-21, -23, -25, -27, -28, -29,
|
|
JUMP_PEAK, JUMP_PEAK, JUMP_PEAK, JUMP_PEAK,
|
|
-29, -29, -28, -28, -27, -27, -26, -25,
|
|
-24, -22, -20, -18, -16, -14, -12, -11,
|
|
-9, -6, -4, -1},
|
|
};
|
|
|
|
static void UpdateJump(int multiplayerId)
|
|
{
|
|
int jumpOffsetIdx;
|
|
int jumpOffset;
|
|
struct PokemonJump_Player *player;
|
|
|
|
if (sPokemonJump->skipJumpUpdate) // Always false
|
|
return;
|
|
|
|
player = &sPokemonJump->players[multiplayerId];
|
|
if (player->jumpOffsetIdx != INT_MAX)
|
|
{
|
|
player->jumpOffsetIdx++;
|
|
jumpOffsetIdx = player->jumpOffsetIdx;
|
|
}
|
|
else
|
|
{
|
|
jumpOffsetIdx = sPokemonJump->vineTimer - player->jumpTimeStart;
|
|
if (jumpOffsetIdx >= 65000)
|
|
{
|
|
jumpOffsetIdx -= 65000;
|
|
jumpOffsetIdx += sPokemonJump->vineTimer;
|
|
}
|
|
|
|
player->jumpOffsetIdx = jumpOffsetIdx;
|
|
}
|
|
|
|
if (jumpOffsetIdx < 4)
|
|
return;
|
|
|
|
jumpOffsetIdx -= 4;
|
|
if (jumpOffsetIdx < (int)ARRAY_COUNT(sJumpOffsets[0]))
|
|
jumpOffset = sJumpOffsets[player->monJumpType][jumpOffsetIdx];
|
|
else
|
|
jumpOffset = 0;
|
|
|
|
SetMonSpriteY(multiplayerId, jumpOffset);
|
|
if (jumpOffset == 0 && multiplayerId == sPokemonJump->multiplayerId)
|
|
SetMonStateNormal();
|
|
|
|
player->jumpOffset = jumpOffset;
|
|
}
|
|
|
|
static void TryUpdateScore(void)
|
|
{
|
|
if (sPokemonJump->vineState == VINE_UPSWING_HIGH && sPokemonJump->prevVineState == VINE_UPSWING_LOW)
|
|
{
|
|
// Vine has passed through the point where it
|
|
// would hit the players, allow score to update
|
|
|
|
if (!sPokemonJump->initScoreUpdate)
|
|
{
|
|
ClearUnreadField();
|
|
sPokemonJump->numPlayersAtPeak = 0;
|
|
sPokemonJump->initScoreUpdate = TRUE;
|
|
sPokemonJump->comm.receivedBonusFlags = 0;
|
|
}
|
|
else
|
|
{
|
|
if (sPokemonJump->numPlayersAtPeak == MAX_RFU_PLAYERS)
|
|
{
|
|
// An 'excellent' is the max 5 players all jumping synchronously
|
|
sPokemonJump->excellentsInRow++;
|
|
TryUpdateExcellentsRecord(sPokemonJump->excellentsInRow);
|
|
}
|
|
else
|
|
{
|
|
sPokemonJump->excellentsInRow = 0;
|
|
}
|
|
|
|
if (sPokemonJump->numPlayersAtPeak > 1)
|
|
{
|
|
sPokemonJump->giveBonus = TRUE;
|
|
// Unclear why atJumpPeak needed to be copied over twice
|
|
memcpy(sPokemonJump->atJumpPeak3, sPokemonJump->atJumpPeak2, sizeof(u8) * MAX_RFU_PLAYERS);
|
|
}
|
|
|
|
ClearUnreadField();
|
|
sPokemonJump->numPlayersAtPeak = 0;
|
|
sPokemonJump->initScoreUpdate = TRUE;
|
|
sPokemonJump->comm.receivedBonusFlags = 0;
|
|
if (sPokemonJump->comm.jumpsInRow < MAX_JUMPS)
|
|
sPokemonJump->comm.jumpsInRow++;
|
|
|
|
AddJumpScore(10);
|
|
SetLinkTimeInterval(LINK_INTERVAL_SHORT);
|
|
}
|
|
}
|
|
|
|
if (sPokemonJump->giveBonus && (DidAllPlayersClearVine() == TRUE || sPokemonJump->vineState == VINE_HIGHEST))
|
|
{
|
|
int numPlayers = GetNumPlayersForBonus(sPokemonJump->atJumpPeak3);
|
|
AddJumpScore(GetScoreBonus(numPlayers));
|
|
SetLinkTimeInterval(LINK_INTERVAL_SHORT);
|
|
sPokemonJump->giveBonus = FALSE;
|
|
}
|
|
|
|
if (sPokemonJump->initScoreUpdate)
|
|
{
|
|
int numAtPeak = GetPlayersAtJumpPeak();
|
|
if (numAtPeak > sPokemonJump->numPlayersAtPeak)
|
|
{
|
|
sPokemonJump->numPlayersAtPeak = numAtPeak;
|
|
memcpy(sPokemonJump->atJumpPeak2, sPokemonJump->atJumpPeak, sizeof(u8) * MAX_RFU_PLAYERS);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns FALSE if any player was hit by vine
|
|
static bool32 UpdateVineHitStates(void)
|
|
{
|
|
int i;
|
|
|
|
if (sPokemonJump->vineState == VINE_UPSWING_LOWER && sPokemonJump->player->jumpOffset == 0)
|
|
{
|
|
// Vine is in position to hit the player and jump offset is 0.
|
|
// Unless the player had just jumped and has been forced to the ground
|
|
// by someone else getting hit, the player has been hit
|
|
if (sPokemonJump->player->prevMonState == MONSTATE_JUMP && IsGameOver() == TRUE)
|
|
{
|
|
sPokemonJump->player->jumpState = JUMPSTATE_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// Hit vine
|
|
SetMonStateHit();
|
|
SetLinkTimeInterval(LINK_INTERVAL_SHORT);
|
|
}
|
|
}
|
|
|
|
if (sPokemonJump->vineState == VINE_UPSWING_LOW
|
|
&& sPokemonJump->prevVineState == VINE_UPSWING_LOWER
|
|
&& sPokemonJump->player->monState != MONSTATE_HIT)
|
|
{
|
|
sPokemonJump->player->jumpState = JUMPSTATE_SUCCESS;
|
|
SetLinkTimeInterval(LINK_INTERVAL_SHORT);
|
|
}
|
|
|
|
for (i = 0; i < sPokemonJump->numPlayers; i++)
|
|
{
|
|
if (sPokemonJump->players[i].monState == MONSTATE_HIT)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Has everyone either jumped or been hit by the vine
|
|
static bool32 AllPlayersJumpedOrHit(void)
|
|
{
|
|
int i;
|
|
int numPlayers = sPokemonJump->numPlayers;
|
|
int numJumpedOrHit = 0;
|
|
for (i = 0; i < numPlayers; i++)
|
|
{
|
|
if (sPokemonJump->players[i].jumpState != JUMPSTATE_NONE)
|
|
numJumpedOrHit++;
|
|
}
|
|
|
|
return numJumpedOrHit == numPlayers;
|
|
}
|
|
|
|
static bool32 DidAllPlayersClearVine(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < sPokemonJump->numPlayers; i++)
|
|
{
|
|
if (sPokemonJump->players[i].jumpState != JUMPSTATE_SUCCESS)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 ShouldPlayAgain(void)
|
|
{
|
|
int i;
|
|
|
|
if (sPokemonJump->playAgainState == PLAY_AGAIN_NO)
|
|
return FALSE;
|
|
|
|
for (i = 1; i < sPokemonJump->numPlayers; i++)
|
|
{
|
|
if (sPokemonJump->playAgainStates[i] == PLAY_AGAIN_NO)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void AddJumpScore(int score)
|
|
{
|
|
sPokemonJump->comm.jumpScore += score;
|
|
sPokemonJump->updateScore = TRUE;
|
|
if (sPokemonJump->comm.jumpScore >= MAX_JUMP_SCORE)
|
|
sPokemonJump->comm.jumpScore = MAX_JUMP_SCORE;
|
|
}
|
|
|
|
static int GetPlayersAtJumpPeak(void)
|
|
{
|
|
int i;
|
|
int numAtPeak = 0;
|
|
int numPlayers = sPokemonJump->numPlayers;
|
|
|
|
for (i = 0; i < numPlayers; i++)
|
|
{
|
|
if (sPokemonJump->players[i].jumpOffset == JUMP_PEAK)
|
|
{
|
|
sPokemonJump->atJumpPeak[i] = TRUE;
|
|
numAtPeak++;
|
|
}
|
|
else
|
|
{
|
|
sPokemonJump->atJumpPeak[i] = FALSE;
|
|
}
|
|
}
|
|
|
|
return numAtPeak;
|
|
}
|
|
|
|
static bool32 AreLinkQueuesEmpty(void)
|
|
{
|
|
return !Rfu.recvQueue.count && !Rfu.sendQueue.count;
|
|
}
|
|
|
|
static int GetNumPlayersForBonus(u8 *arg0)
|
|
{
|
|
int i = 0;
|
|
int flags = 0;
|
|
int count = 0;
|
|
|
|
for (; i < MAX_RFU_PLAYERS; i++)
|
|
{
|
|
if (arg0[i])
|
|
{
|
|
flags |= 1 << i;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
sPokemonJump->comm.receivedBonusFlags = flags;
|
|
if (flags)
|
|
sPokemonJump->showBonus = TRUE;
|
|
|
|
return count;
|
|
}
|
|
|
|
static void ClearUnreadField(void)
|
|
{
|
|
sPokemonJump->unused3 = 0;
|
|
}
|
|
|
|
// Bonuses given depend on the number of
|
|
// players that jumped at the same time
|
|
static const int sScoreBonuses[MAX_RFU_PLAYERS + 1] = {0, 0, 50, 100, 200, 500};
|
|
|
|
static int GetScoreBonus(int numPlayers)
|
|
{
|
|
return sScoreBonuses[numPlayers];
|
|
}
|
|
|
|
static void TryUpdateExcellentsRecord(u16 excellentsInRow)
|
|
{
|
|
if (excellentsInRow > sPokemonJump->excellentsInRowRecord)
|
|
sPokemonJump->excellentsInRowRecord = excellentsInRow;
|
|
}
|
|
|
|
static const u16 sPrizeItems[] = {
|
|
ITEM_LEPPA_BERRY,
|
|
ITEM_LUM_BERRY,
|
|
ITEM_SITRUS_BERRY,
|
|
ITEM_FIGY_BERRY,
|
|
ITEM_WIKI_BERRY,
|
|
ITEM_MAGO_BERRY,
|
|
ITEM_AGUAV_BERRY,
|
|
ITEM_IAPAPA_BERRY
|
|
};
|
|
|
|
struct {
|
|
u32 score;
|
|
u32 quantity;
|
|
} static const sPrizeQuantityData[] =
|
|
{
|
|
{ .score = 5000, .quantity = 1},
|
|
{ .score = 8000, .quantity = 2},
|
|
{ .score = 12000, .quantity = 3},
|
|
{ .score = 16000, .quantity = 4},
|
|
{ .score = 20000, .quantity = 5},
|
|
};
|
|
|
|
static bool32 HasEnoughScoreForPrize(void)
|
|
{
|
|
if (sPokemonJump->comm.jumpScore >= sPrizeQuantityData[0].score)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
static u16 GetPrizeData(void)
|
|
{
|
|
u16 itemId = GetPrizeItemId();
|
|
u16 quantity = GetPrizeQuantity();
|
|
return (quantity << 12) | (itemId & 0xFFF);
|
|
}
|
|
|
|
static void UnpackPrizeData(u16 data, u16 *itemId, u16 *quantity)
|
|
{
|
|
*quantity = data >> 12;
|
|
*itemId = data & 0xFFF;
|
|
}
|
|
|
|
static u16 GetPrizeItemId(void)
|
|
{
|
|
u16 index = Random() % ARRAY_COUNT(sPrizeItems);
|
|
return sPrizeItems[index];
|
|
}
|
|
|
|
static u16 GetPrizeQuantity(void)
|
|
{
|
|
u32 quantity, i;
|
|
|
|
quantity = 0;
|
|
for (i = 0; i < ARRAY_COUNT(sPrizeQuantityData); i++)
|
|
{
|
|
if (sPokemonJump->comm.jumpScore >= sPrizeQuantityData[i].score)
|
|
quantity = sPrizeQuantityData[i].quantity;
|
|
else
|
|
break;
|
|
}
|
|
|
|
return quantity;
|
|
}
|
|
|
|
static u16 GetQuantityLimitedByBag(u16 item, u16 quantity)
|
|
{
|
|
while (quantity && !CheckBagHasSpace(item, quantity))
|
|
quantity--;
|
|
|
|
return quantity;
|
|
}
|
|
|
|
static u16 GetNumPokeJumpPlayers(void)
|
|
{
|
|
return GetLinkPlayerCount();
|
|
}
|
|
|
|
static u16 GetPokeJumpMultiplayerId(void)
|
|
{
|
|
return sPokemonJump->multiplayerId;
|
|
}
|
|
|
|
static struct PokemonJump_MonInfo *GetMonInfoByMultiplayerId(u8 multiplayerId)
|
|
{
|
|
return &sPokemonJump->monInfo[multiplayerId];
|
|
}
|
|
|
|
static u8 *GetPokeJumpPlayerName(u8 multiplayerId)
|
|
{
|
|
return sPokemonJump->players[multiplayerId].name;
|
|
}
|
|
|
|
bool32 IsSpeciesAllowedInPokemonJump(u16 species)
|
|
{
|
|
return GetPokemonJumpSpeciesIdx(species) > -1;
|
|
}
|
|
|
|
void IsPokemonJumpSpeciesInParty(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < PARTY_SIZE; i++)
|
|
{
|
|
if (GetMonData(&gPlayerParty[i], MON_DATA_SANITY_HAS_SPECIES))
|
|
{
|
|
u16 species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2);
|
|
if (IsSpeciesAllowedInPokemonJump(species))
|
|
{
|
|
gSpecialVar_Result = TRUE;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
gSpecialVar_Result = FALSE;
|
|
}
|
|
|
|
static const u16 sPokeJumpPal1[] = INCBIN_U16("graphics/pokemon_jump/pal1.gbapal");
|
|
static const u16 sPokeJumpPal2[] = INCBIN_U16("graphics/pokemon_jump/pal2.gbapal");
|
|
|
|
static const u32 sVine1_Gfx[] = INCBIN_U32("graphics/pokemon_jump/vine1.4bpp.lz");
|
|
static const u32 sVine2_Gfx[] = INCBIN_U32("graphics/pokemon_jump/vine2.4bpp.lz");
|
|
static const u32 sVine3_Gfx[] = INCBIN_U32("graphics/pokemon_jump/vine3.4bpp.lz");
|
|
static const u32 sVine4_Gfx[] = INCBIN_U32("graphics/pokemon_jump/vine4.4bpp.lz");
|
|
|
|
static const u32 sStar_Gfx[] = INCBIN_U32("graphics/pokemon_jump/star.4bpp.lz");
|
|
|
|
static const struct CompressedSpriteSheet sCompressedSpriteSheets[] =
|
|
{
|
|
{sVine1_Gfx, 0x600, GFXTAG_VINE1},
|
|
{sVine2_Gfx, 0xC00, GFXTAG_VINE2},
|
|
{sVine3_Gfx, 0x600, GFXTAG_VINE3},
|
|
{sVine4_Gfx, 0x600, GFXTAG_VINE4},
|
|
{sStar_Gfx, 0x200, GFXTAG_STAR},
|
|
};
|
|
|
|
static const struct SpritePalette sSpritePalettes[] =
|
|
{
|
|
{sPokeJumpPal1, PALTAG_1},
|
|
{sPokeJumpPal2, PALTAG_2},
|
|
};
|
|
|
|
static const struct OamData sOamData_JumpMon;
|
|
static const struct SpriteTemplate sSpriteTemplate_Vine1;
|
|
static const struct SpriteTemplate sSpriteTemplate_Vine2;
|
|
static const struct SpriteTemplate sSpriteTemplate_Vine3;
|
|
static const struct SpriteTemplate sSpriteTemplate_Vine4;
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_JumpMon =
|
|
{
|
|
.tileTag = TAG_MON1,
|
|
.paletteTag = TAG_MON1,
|
|
.oam = &sOamData_JumpMon,
|
|
.anims = gDummySpriteAnimTable,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCallbackDummy,
|
|
};
|
|
|
|
static const s16 sVineYCoords[VINE_SPRITES_PER_SIDE][NUM_VINESTATES] =
|
|
{
|
|
{96, 96, 96, 114, 120, 120, 120, 114, 96, 96},
|
|
{70, 80, 96, 114, 120, 128, 120, 114, 96, 80},
|
|
{50, 72, 96, 114, 128, 136, 128, 114, 96, 72},
|
|
{42, 72, 96, 114, 128, 136, 128, 114, 96, 72},
|
|
};
|
|
|
|
static const s16 sVineXCoords[VINE_SPRITES_PER_SIDE * 2] = {16, 40, 72, 104, 136, 168, 200, 224};
|
|
|
|
static const struct SpriteTemplate *const sSpriteTemplates_Vine[VINE_SPRITES_PER_SIDE] =
|
|
{
|
|
&sSpriteTemplate_Vine1,
|
|
&sSpriteTemplate_Vine2,
|
|
&sSpriteTemplate_Vine3,
|
|
&sSpriteTemplate_Vine4,
|
|
};
|
|
|
|
static const struct OamData sOamData_JumpMon =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = 0,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(64x64),
|
|
.x = 0,
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(64x64),
|
|
.tileNum = 0,
|
|
.priority = 2,
|
|
.paletteNum = 0,
|
|
.affineParam = 0
|
|
};
|
|
|
|
static const struct OamData sOamData_Vine16x32 =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = 0,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(16x32),
|
|
.x = 0,
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(16x32),
|
|
.tileNum = 0,
|
|
.priority = 2,
|
|
.paletteNum = 0,
|
|
.affineParam = 0
|
|
};
|
|
|
|
static const struct OamData sOamData_Vine32x32 =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = 0,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(32x32),
|
|
.x = 0,
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(32x32),
|
|
.tileNum = 0,
|
|
.priority = 2,
|
|
.paletteNum = 0,
|
|
.affineParam = 0
|
|
};
|
|
|
|
static const struct OamData sOamData_Vine32x16 =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = 0,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(32x16),
|
|
.x = 0,
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(32x16),
|
|
.tileNum = 0,
|
|
.priority = 2,
|
|
.paletteNum = 0,
|
|
.affineParam = 0
|
|
};
|
|
|
|
static const union AnimCmd sAnims_Vine_Highest[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnims_Vine_Higher[] =
|
|
{
|
|
ANIMCMD_FRAME(8, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnims_Vine_High[] =
|
|
{
|
|
ANIMCMD_FRAME(16, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnims_Vine_Low[] =
|
|
{
|
|
ANIMCMD_FRAME(24, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnims_Vine_Lower[] =
|
|
{
|
|
ANIMCMD_FRAME(32, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnims_Vine_Lowest[] =
|
|
{
|
|
ANIMCMD_FRAME(40, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnims_VineTall_Highest[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnims_VineTall_Higher[] =
|
|
{
|
|
ANIMCMD_FRAME(16, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnims_VineTall_High[] =
|
|
{
|
|
ANIMCMD_FRAME(32, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnims_VineTall_Low[] =
|
|
{
|
|
ANIMCMD_FRAME(48, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnims_VineTall_Lower[] =
|
|
{
|
|
ANIMCMD_FRAME(64, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnims_VineTall_Lowest[] =
|
|
{
|
|
ANIMCMD_FRAME(80, 1),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd *const sAnims_Vine[] =
|
|
{
|
|
sAnims_Vine_Highest,
|
|
sAnims_Vine_Higher,
|
|
sAnims_Vine_High,
|
|
sAnims_Vine_Low,
|
|
sAnims_Vine_Lower,
|
|
sAnims_Vine_Lowest
|
|
};
|
|
|
|
// Vine 2 needs its own set of anims because the graphic is twice as large
|
|
static const union AnimCmd *const sAnims_VineTall[] =
|
|
{
|
|
sAnims_VineTall_Highest,
|
|
sAnims_VineTall_Higher,
|
|
sAnims_VineTall_High,
|
|
sAnims_VineTall_Low,
|
|
sAnims_VineTall_Lower,
|
|
sAnims_VineTall_Lowest
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_Vine1 =
|
|
{
|
|
.tileTag = GFXTAG_VINE1,
|
|
.paletteTag = PALTAG_1,
|
|
.oam = &sOamData_Vine16x32,
|
|
.anims = sAnims_Vine,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCallbackDummy,
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_Vine2 =
|
|
{
|
|
.tileTag = GFXTAG_VINE2,
|
|
.paletteTag = PALTAG_1,
|
|
.oam = &sOamData_Vine32x32,
|
|
.anims = sAnims_VineTall,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCallbackDummy,
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_Vine3 =
|
|
{
|
|
.tileTag = GFXTAG_VINE3,
|
|
.paletteTag = PALTAG_1,
|
|
.oam = &sOamData_Vine32x16,
|
|
.anims = sAnims_Vine,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCallbackDummy,
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_Vine4 =
|
|
{
|
|
.tileTag = GFXTAG_VINE4,
|
|
.paletteTag = PALTAG_1,
|
|
.oam = &sOamData_Vine32x16,
|
|
.anims = sAnims_Vine,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCallbackDummy,
|
|
};
|
|
|
|
static const struct OamData sOamData_Star =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = 0,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(16x16),
|
|
.x = 0,
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(16x16),
|
|
.tileNum = 0,
|
|
.priority = 1,
|
|
.paletteNum = 0,
|
|
.affineParam = 0
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Star_Still[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 0),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd sAnim_Star_Spinning[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 4),
|
|
ANIMCMD_FRAME(4, 4),
|
|
ANIMCMD_FRAME(8, 4),
|
|
ANIMCMD_FRAME(12, 4),
|
|
ANIMCMD_LOOP(1),
|
|
ANIMCMD_FRAME(0, 4),
|
|
ANIMCMD_END
|
|
};
|
|
|
|
static const union AnimCmd *const sAnims_Star[] =
|
|
{
|
|
sAnim_Star_Still,
|
|
sAnim_Star_Spinning
|
|
};
|
|
|
|
static const struct SpriteTemplate sSpriteTemplate_Star =
|
|
{
|
|
.tileTag = GFXTAG_STAR,
|
|
.paletteTag = PALTAG_1,
|
|
.oam = &sOamData_Star,
|
|
.anims = sAnims_Star,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = SpriteCallbackDummy,
|
|
};
|
|
|
|
static void LoadSpriteSheetsAndPalettes(struct PokemonJumpGfx *jumpGfx)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(sCompressedSpriteSheets); i++)
|
|
LoadCompressedSpriteSheet(&sCompressedSpriteSheets[i]);
|
|
|
|
for (i = 0; i < ARRAY_COUNT(sSpritePalettes); i++)
|
|
LoadSpritePalette(&sSpritePalettes[i]);
|
|
|
|
jumpGfx->vinePalNumDownswing = IndexOfSpritePaletteTag(PALTAG_1);
|
|
jumpGfx->vinePalNumUpswing = IndexOfSpritePaletteTag(PALTAG_2);
|
|
}
|
|
|
|
static void ResetPokeJumpSpriteData(struct Sprite *sprite)
|
|
{
|
|
int i;
|
|
for (i = 0; i < (int)ARRAY_COUNT(sprite->data); i++)
|
|
sprite->data[i] = 0;
|
|
}
|
|
|
|
static void CreateJumpMonSprite(struct PokemonJumpGfx *jumpGfx, struct PokemonJump_MonInfo *monInfo, s16 x, s16 y, u8 multiplayerId)
|
|
{
|
|
struct SpriteTemplate spriteTemplate;
|
|
struct SpriteSheet spriteSheet;
|
|
struct CompressedSpritePalette spritePalette;
|
|
u8 *buffer;
|
|
u8 *unusedBuffer;
|
|
u8 subpriority;
|
|
u8 spriteId;
|
|
|
|
spriteTemplate = sSpriteTemplate_JumpMon;
|
|
buffer = Alloc(0x2000);
|
|
unusedBuffer = Alloc(MON_PIC_SIZE);
|
|
if (multiplayerId == GetPokeJumpMultiplayerId())
|
|
subpriority = 3;
|
|
else
|
|
subpriority = multiplayerId + 4;
|
|
|
|
if (buffer && unusedBuffer)
|
|
{
|
|
HandleLoadSpecialPokePic(
|
|
&gMonStillFrontPicTable[monInfo->species],
|
|
buffer,
|
|
monInfo->species,
|
|
monInfo->personality);
|
|
|
|
spriteSheet.data = buffer;
|
|
spriteSheet.tag = multiplayerId;
|
|
spriteSheet.size = MON_PIC_SIZE;
|
|
LoadSpriteSheet(&spriteSheet);
|
|
|
|
spritePalette.data = GetMonSpritePalFromSpeciesAndPersonality(monInfo->species, monInfo->otId, monInfo->personality);
|
|
spritePalette.tag = multiplayerId;
|
|
LoadCompressedSpritePalette(&spritePalette);
|
|
|
|
Free(buffer);
|
|
Free(unusedBuffer);
|
|
|
|
spriteTemplate.tileTag += multiplayerId;
|
|
spriteTemplate.paletteTag += multiplayerId;
|
|
spriteId = CreateSprite(&spriteTemplate, x, y, subpriority);
|
|
if (spriteId != MAX_SPRITES)
|
|
{
|
|
jumpGfx->monSprites[multiplayerId] = &gSprites[spriteId];
|
|
jumpGfx->monSpriteSubpriorities[multiplayerId] = subpriority;
|
|
return;
|
|
}
|
|
}
|
|
|
|
jumpGfx->monSprites[multiplayerId] = NULL;
|
|
}
|
|
|
|
#define sState data[0]
|
|
#define sTimer data[1]
|
|
#define sOffset data[7] // Never read
|
|
|
|
static void DoStarAnim(struct PokemonJumpGfx *jumpGfx, int multiplayerId)
|
|
{
|
|
ResetPokeJumpSpriteData(jumpGfx->starSprites[multiplayerId]);
|
|
jumpGfx->starSprites[multiplayerId]->sOffset = jumpGfx->monSprites[multiplayerId] - gSprites;
|
|
jumpGfx->starSprites[multiplayerId]->invisible = FALSE;
|
|
jumpGfx->starSprites[multiplayerId]->y = 96;
|
|
jumpGfx->starSprites[multiplayerId]->callback = SpriteCB_Star;
|
|
StartSpriteAnim(jumpGfx->starSprites[multiplayerId], 1);
|
|
}
|
|
|
|
static void SpriteCB_Star(struct Sprite *sprite)
|
|
{
|
|
switch (sprite->sState)
|
|
{
|
|
case 0:
|
|
if (sprite->animEnded)
|
|
{
|
|
sprite->invisible = TRUE;
|
|
sprite->callback = SpriteCallbackDummy;
|
|
}
|
|
break;
|
|
case 1:
|
|
sprite->y--;
|
|
sprite->sTimer++;
|
|
if (sprite->y <= 72)
|
|
{
|
|
sprite->y = 72;
|
|
sprite->sState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (++sprite->sTimer >= 48)
|
|
{
|
|
sprite->invisible = TRUE;
|
|
sprite->callback = SpriteCallbackDummy;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#undef sState
|
|
#undef sTimer
|
|
#undef sOffset
|
|
|
|
static void Gfx_StartMonHitShake(struct PokemonJumpGfx *jumpGfx, int multiplayerId)
|
|
{
|
|
jumpGfx->monSprites[multiplayerId]->callback = SpriteCB_MonHitShake;
|
|
jumpGfx->monSprites[multiplayerId]->y2 = 0;
|
|
ResetPokeJumpSpriteData(jumpGfx->monSprites[multiplayerId]);
|
|
}
|
|
|
|
static bool32 Gfx_IsMonHitShakeActive(struct PokemonJumpGfx *jumpGfx, int multiplayerId)
|
|
{
|
|
return jumpGfx->monSprites[multiplayerId]->callback == SpriteCB_MonHitShake;
|
|
}
|
|
|
|
#define sTimer data[1]
|
|
#define sNumShakes data[2]
|
|
|
|
static void SpriteCB_MonHitShake(struct Sprite *sprite)
|
|
{
|
|
if (++sprite->sTimer > 1)
|
|
{
|
|
if (++sprite->sNumShakes & 1)
|
|
sprite->y2 = 2;
|
|
else
|
|
sprite->y2 = -2;
|
|
|
|
sprite->sTimer = 0;
|
|
}
|
|
|
|
if (sprite->sNumShakes > 12)
|
|
{
|
|
sprite->y2 = 0;
|
|
sprite->callback = SpriteCallbackDummy;
|
|
}
|
|
}
|
|
|
|
#undef sTimer
|
|
#undef sNumShakes
|
|
|
|
static void Gfx_StartMonHitFlash(struct PokemonJumpGfx *jumpGfx, int multiplayerId)
|
|
{
|
|
ResetPokeJumpSpriteData(jumpGfx->monSprites[multiplayerId]);
|
|
jumpGfx->monSprites[multiplayerId]->callback = SpriteCB_MonHitFlash;
|
|
}
|
|
|
|
static void Gfx_StopMonHitFlash(struct PokemonJumpGfx *jumpGfx)
|
|
{
|
|
int i;
|
|
u16 numPlayers = GetNumPokeJumpPlayers();
|
|
for (i = 0; i < numPlayers; i++)
|
|
{
|
|
if (jumpGfx->monSprites[i]->callback == SpriteCB_MonHitFlash)
|
|
{
|
|
jumpGfx->monSprites[i]->invisible = FALSE;
|
|
jumpGfx->monSprites[i]->callback = SpriteCallbackDummy;
|
|
jumpGfx->monSprites[i]->subpriority = 10;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define sTimer data[0]
|
|
|
|
static void SpriteCB_MonHitFlash(struct Sprite *sprite)
|
|
{
|
|
if (++sprite->sTimer > 3)
|
|
{
|
|
sprite->sTimer = 0;
|
|
sprite->invisible ^= 1;
|
|
}
|
|
}
|
|
|
|
#undef sTimer
|
|
|
|
static void Gfx_ResetMonSpriteSubpriorities(struct PokemonJumpGfx *jumpGfx)
|
|
{
|
|
int i;
|
|
u16 numPlayers = GetNumPokeJumpPlayers();
|
|
for (i = 0; i < numPlayers; i++)
|
|
jumpGfx->monSprites[i]->subpriority = jumpGfx->monSpriteSubpriorities[i];
|
|
}
|
|
|
|
static void Gfx_StartMonIntroBounce(struct PokemonJumpGfx *jumpGfx, int multiplayerId)
|
|
{
|
|
ResetPokeJumpSpriteData(jumpGfx->monSprites[multiplayerId]);
|
|
jumpGfx->monSprites[multiplayerId]->callback = SpriteCB_MonIntroBounce;
|
|
}
|
|
|
|
static bool32 Gfx_IsMonIntroBounceActive(struct PokemonJumpGfx *jumpGfx)
|
|
{
|
|
int i;
|
|
u16 numPlayers = GetNumPokeJumpPlayers();
|
|
for (i = 0; i < numPlayers; i++)
|
|
{
|
|
if (jumpGfx->monSprites[i]->callback == SpriteCB_MonIntroBounce)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#define sState data[0]
|
|
#define sHopPos data[1]
|
|
#define sNumHops data[2]
|
|
|
|
static void SpriteCB_MonIntroBounce(struct Sprite *sprite)
|
|
{
|
|
switch (sprite->sState)
|
|
{
|
|
case 0:
|
|
PlaySE(SE_BIKE_HOP);
|
|
sprite->sHopPos = 0;
|
|
sprite->sState++;
|
|
// fall through
|
|
case 1:
|
|
sprite->sHopPos += 4;
|
|
if (sprite->sHopPos > 127)
|
|
sprite->sHopPos = 0;
|
|
|
|
sprite->y2 = -(gSineTable[sprite->sHopPos] >> 3);
|
|
if (sprite->sHopPos == 0)
|
|
{
|
|
if (++sprite->sNumHops < 2)
|
|
sprite->sState = 0;
|
|
else
|
|
sprite->callback = SpriteCallbackDummy;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#undef sState
|
|
#undef sHopPos
|
|
#undef sNumHops
|
|
|
|
static void CreateStarSprite(struct PokemonJumpGfx *jumpGfx, s16 x, s16 y, u8 multiplayerId)
|
|
{
|
|
u8 spriteId = CreateSprite(&sSpriteTemplate_Star, x, y, 1);
|
|
if (spriteId != MAX_SPRITES)
|
|
{
|
|
gSprites[spriteId].invisible = TRUE;
|
|
jumpGfx->starSprites[multiplayerId] = &gSprites[spriteId];
|
|
}
|
|
}
|
|
|
|
static void CreateVineSprites(struct PokemonJumpGfx *jumpGfx)
|
|
{
|
|
int i;
|
|
int count;
|
|
u8 spriteId;
|
|
|
|
count = 0;
|
|
for (i = 0; i < VINE_SPRITES_PER_SIDE; i++)
|
|
{
|
|
spriteId = CreateSprite(sSpriteTemplates_Vine[i], sVineXCoords[count], sVineYCoords[i][0], 2);
|
|
jumpGfx->vineSprites[count] = &gSprites[spriteId];
|
|
count++;
|
|
}
|
|
|
|
for (i = VINE_SPRITES_PER_SIDE - 1; i >= 0; i--)
|
|
{
|
|
spriteId = CreateSprite(sSpriteTemplates_Vine[i], sVineXCoords[count], sVineYCoords[i][0], 2);
|
|
jumpGfx->vineSprites[count] = &gSprites[spriteId];
|
|
jumpGfx->vineSprites[count]->hFlip = TRUE;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
static void UpdateVineAnim(struct PokemonJumpGfx *jumpGfx, int vineState)
|
|
{
|
|
int i, count, palNum;
|
|
int priority;
|
|
|
|
if (vineState > VINE_LOWEST)
|
|
{
|
|
// animNums for vine on upswing are same as
|
|
// on downswing but in reverse
|
|
vineState = NUM_VINESTATES - vineState;
|
|
priority = 3; // Set vine behind Pokémon
|
|
palNum = jumpGfx->vinePalNumUpswing;
|
|
}
|
|
else
|
|
{
|
|
priority = 2; // Set vine in front of Pokémon
|
|
palNum = jumpGfx->vinePalNumDownswing;
|
|
}
|
|
|
|
count = 0;
|
|
for (i = 0; i < VINE_SPRITES_PER_SIDE; i++)
|
|
{
|
|
jumpGfx->vineSprites[count]->y = sVineYCoords[i][vineState];
|
|
jumpGfx->vineSprites[count]->oam.priority = priority;
|
|
jumpGfx->vineSprites[count]->oam.paletteNum = palNum;
|
|
StartSpriteAnim(jumpGfx->vineSprites[count], vineState);
|
|
count++;
|
|
}
|
|
|
|
for (i = VINE_SPRITES_PER_SIDE - 1; i >= 0; i--)
|
|
{
|
|
jumpGfx->vineSprites[count]->y = sVineYCoords[i][vineState];
|
|
jumpGfx->vineSprites[count]->oam.priority = priority;
|
|
jumpGfx->vineSprites[count]->oam.paletteNum = palNum;
|
|
StartSpriteAnim(jumpGfx->vineSprites[count], vineState);
|
|
count++;
|
|
}
|
|
}
|
|
|
|
static void StartPokeJumpCountdown(struct PokemonJumpGfx *jumpGfx)
|
|
{
|
|
StartMinigameCountdown(GFXTAG_COUNTDOWN, PALTAG_COUNTDOWN, 120, 80, 0);
|
|
Gfx_ResetMonSpriteSubpriorities(jumpGfx);
|
|
}
|
|
|
|
static bool32 IsPokeJumpCountdownRunning(void)
|
|
{
|
|
return IsMinigameCountdownRunning();
|
|
}
|
|
|
|
static void StartPokeJumpGfx(struct PokemonJumpGfx *jumpGfx)
|
|
{
|
|
u8 taskId;
|
|
|
|
sPokemonJumpGfx = jumpGfx;
|
|
InitPokeJumpGfx(sPokemonJumpGfx);
|
|
taskId = CreateTask(Task_RunPokeJumpGfxFunc, 3);
|
|
sPokemonJumpGfx->taskId = taskId;
|
|
SetWordTaskArg(sPokemonJumpGfx->taskId, 2, (u32) sPokemonJumpGfx);
|
|
SetUpPokeJumpGfxFunc(LoadPokeJumpGfx);
|
|
}
|
|
|
|
static void FreeWindowsAndDigitObj(void)
|
|
{
|
|
FreeAllWindowBuffers();
|
|
DigitObjUtil_Free();
|
|
}
|
|
|
|
static void InitPokeJumpGfx(struct PokemonJumpGfx *jumpGfx)
|
|
{
|
|
jumpGfx->mainState = 0;
|
|
jumpGfx->funcFinished = FALSE;
|
|
jumpGfx->msgWindowId = WINDOW_NONE;
|
|
}
|
|
|
|
static const u16 sInterface_Pal[] = INCBIN_U16("graphics/pokemon_jump/interface.gbapal");
|
|
|
|
static const u16 sBg_Pal[] = INCBIN_U16("graphics/pokemon_jump/bg.gbapal");
|
|
static const u32 sBg_Gfx[] = INCBIN_U32("graphics/pokemon_jump/bg.4bpp.lz");
|
|
static const u32 sBg_Tilemap[] = INCBIN_U32("graphics/pokemon_jump/bg.bin.lz");
|
|
|
|
static const u16 sVenusaur_Pal[] = INCBIN_U16("graphics/pokemon_jump/venusaur.gbapal");
|
|
static const u32 sVenusaur_Gfx[] = INCBIN_U32("graphics/pokemon_jump/venusaur.4bpp.lz");
|
|
static const u32 sVenusaur_Tilemap[] = INCBIN_U32("graphics/pokemon_jump/venusaur.bin.lz");
|
|
|
|
static const u16 sBonuses_Pal[] = INCBIN_U16("graphics/pokemon_jump/bonuses.gbapal");
|
|
static const u32 sBonuses_Gfx[] = INCBIN_U32("graphics/pokemon_jump/bonuses.4bpp.lz");
|
|
static const u32 sBonuses_Tilemap[] = INCBIN_U32("graphics/pokemon_jump/bonuses.bin.lz");
|
|
|
|
static const struct BgTemplate sBgTemplates[] =
|
|
{
|
|
{
|
|
.bg = BG_INTERFACE,
|
|
.charBaseIndex = 0,
|
|
.mapBaseIndex = 27,
|
|
.screenSize = 0,
|
|
.paletteMode = 0,
|
|
.priority = 0,
|
|
.baseTile = 0
|
|
},
|
|
{
|
|
.bg = BG_VENUSAUR,
|
|
.charBaseIndex = 1,
|
|
.mapBaseIndex = 30,
|
|
.screenSize = 2,
|
|
.paletteMode = 0,
|
|
.priority = 2,
|
|
.baseTile = 0
|
|
},
|
|
{
|
|
.bg = BG_BONUSES,
|
|
.charBaseIndex = 2,
|
|
.mapBaseIndex = 12,
|
|
.screenSize = 3,
|
|
.paletteMode = 0,
|
|
.priority = 1,
|
|
.baseTile = 0
|
|
},
|
|
{
|
|
.bg = BG_SCENERY,
|
|
.charBaseIndex = 3,
|
|
.mapBaseIndex = 29,
|
|
.screenSize = 0,
|
|
.paletteMode = 0,
|
|
.priority = 3,
|
|
.baseTile = 0
|
|
},
|
|
};
|
|
|
|
static const struct WindowTemplate sWindowTemplates[] =
|
|
{
|
|
[WIN_POINTS] = {
|
|
.bg = BG_INTERFACE,
|
|
.tilemapLeft = 19,
|
|
.tilemapTop = 0,
|
|
.width = 6,
|
|
.height = 2,
|
|
.paletteNum = 2,
|
|
.baseBlock = 0x13,
|
|
},
|
|
[WIN_TIMES] = {
|
|
.bg = BG_INTERFACE,
|
|
.tilemapLeft = 8,
|
|
.tilemapTop = 0,
|
|
.width = 6,
|
|
.height = 2,
|
|
.paletteNum = 2,
|
|
.baseBlock = 0x1F,
|
|
},
|
|
DUMMY_WIN_TEMPLATE,
|
|
};
|
|
|
|
struct
|
|
{
|
|
int id;
|
|
void (*func)(void);
|
|
} static const sPokeJumpGfxFuncs[] =
|
|
{
|
|
{GFXFUNC_LOAD, LoadPokeJumpGfx}, // Element not used, LoadPokeJumpGfx is passed directly to SetUpPokeJumpGfxFunc
|
|
{GFXFUNC_SHOW_NAMES, PrintPlayerNamesNoHighlight},
|
|
{GFXFUNC_SHOW_NAMES_HIGHLIGHT, PrintPlayerNamesWithHighlight},
|
|
{GFXFUNC_ERASE_NAMES, ErasePlayerNames},
|
|
{GFXFUNC_MSG_PLAY_AGAIN, Msg_WantToPlayAgain},
|
|
{GFXFUNC_MSG_SAVING, Msg_SavingDontTurnOff},
|
|
{GFXFUNC_ERASE_MSG, EraseMessage},
|
|
{GFXFUNC_MSG_PLAYER_DROPPED, Msg_SomeoneDroppedOut},
|
|
{GFXFUNC_COUNTDOWN, DoPokeJumpCountdown},
|
|
{GFXFUNC_MSG_COMM_STANDBY, Msg_CommunicationStandby},
|
|
};
|
|
|
|
static void SetUpPokeJumpGfxFuncById(int id)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(sPokeJumpGfxFuncs); i++)
|
|
{
|
|
if (sPokeJumpGfxFuncs[i].id == id)
|
|
SetUpPokeJumpGfxFunc(sPokeJumpGfxFuncs[i].func);
|
|
}
|
|
}
|
|
|
|
static bool32 IsPokeJumpGfxFuncFinished(void)
|
|
{
|
|
return (sPokemonJumpGfx->funcFinished != TRUE);
|
|
}
|
|
|
|
static void SetUpPokeJumpGfxFunc(void (*func)(void))
|
|
{
|
|
SetWordTaskArg(sPokemonJumpGfx->taskId, 0, (u32) func);
|
|
sPokemonJumpGfx->mainState = 0;
|
|
sPokemonJumpGfx->funcFinished = FALSE;
|
|
}
|
|
|
|
static void Task_RunPokeJumpGfxFunc(u8 taskId)
|
|
{
|
|
if (!sPokemonJumpGfx->funcFinished)
|
|
{
|
|
// Read the function set in the data by SetUpPokeJumpGfxFunc
|
|
void (*func)(void) = (void *)(GetWordTaskArg(taskId, 0));
|
|
|
|
func();
|
|
}
|
|
}
|
|
|
|
static void LoadPokeJumpGfx(void)
|
|
{
|
|
switch (sPokemonJumpGfx->mainState)
|
|
{
|
|
case 0:
|
|
ResetBgsAndClearDma3BusyFlags(0);
|
|
InitBgsFromTemplates(0, sBgTemplates, ARRAY_COUNT(sBgTemplates));
|
|
InitWindows(sWindowTemplates);
|
|
ResetTempTileDataBuffers();
|
|
LoadSpriteSheetsAndPalettes(sPokemonJumpGfx);
|
|
InitDigitPrinters();
|
|
LoadPalette(sBg_Pal, 0, 0x20);
|
|
DecompressAndCopyTileDataToVram(BG_SCENERY, sBg_Gfx, 0, 0, 0);
|
|
DecompressAndCopyTileDataToVram(BG_SCENERY, sBg_Tilemap, 0, 0, 1);
|
|
LoadPalette(sVenusaur_Pal, 0x30, 0x20);
|
|
DecompressAndCopyTileDataToVram(BG_VENUSAUR, sVenusaur_Gfx, 0, 0, 0);
|
|
DecompressAndCopyTileDataToVram(BG_VENUSAUR, sVenusaur_Tilemap, 0, 0, 1);
|
|
LoadPalette(sBonuses_Pal, 0x10, 0x20);
|
|
DecompressAndCopyTileDataToVram(BG_BONUSES, sBonuses_Gfx, 0, 0, 0);
|
|
DecompressAndCopyTileDataToVram(BG_BONUSES, sBonuses_Tilemap, 0, 0, 1);
|
|
LoadPalette(sInterface_Pal, 0x20, 0x20);
|
|
SetBgTilemapBuffer(BG_INTERFACE, sPokemonJumpGfx->tilemapBuffer);
|
|
FillBgTilemapBufferRect_Palette0(BG_INTERFACE, 0, 0, 0, 0x20, 0x20);
|
|
PrintScoreSuffixes();
|
|
PrintScore(0);
|
|
sub_8098C6C(0, 1, 0xE0);
|
|
CopyBgTilemapBufferToVram(BG_INTERFACE);
|
|
CopyBgTilemapBufferToVram(BG_VENUSAUR);
|
|
CopyBgTilemapBufferToVram(BG_BONUSES);
|
|
ResetBgPositions();
|
|
sPokemonJumpGfx->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!FreeTempTileDataBuffersIfPossible())
|
|
{
|
|
CreateJumpMonSprites();
|
|
CreateVineSprites(sPokemonJumpGfx);
|
|
UpdateVineAnim(sPokemonJumpGfx, VINE_UPSWING_LOWER);
|
|
ShowBg(BG_SCENERY);
|
|
ShowBg(BG_INTERFACE);
|
|
ShowBg(BG_VENUSAUR);
|
|
HideBg(BG_BONUSES);
|
|
sPokemonJumpGfx->mainState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
sPokemonJumpGfx->funcFinished = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void PrintPlayerNamesNoHighlight(void)
|
|
{
|
|
switch (sPokemonJumpGfx->mainState)
|
|
{
|
|
case 0:
|
|
AddPlayerNameWindows();
|
|
sPokemonJumpGfx->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
PrintPokeJumpPlayerNames(FALSE);
|
|
sPokemonJumpGfx->mainState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
DrawPlayerNameWindows();
|
|
sPokemonJumpGfx->mainState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
sPokemonJumpGfx->funcFinished = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void PrintPlayerNamesWithHighlight(void)
|
|
{
|
|
switch (sPokemonJumpGfx->mainState)
|
|
{
|
|
case 0:
|
|
AddPlayerNameWindows();
|
|
sPokemonJumpGfx->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
PrintPokeJumpPlayerNames(TRUE);
|
|
sPokemonJumpGfx->mainState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
DrawPlayerNameWindows();
|
|
sPokemonJumpGfx->mainState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
sPokemonJumpGfx->funcFinished = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void ErasePlayerNames(void)
|
|
{
|
|
int i, numPlayers;
|
|
|
|
numPlayers = GetNumPokeJumpPlayers();
|
|
switch (sPokemonJumpGfx->mainState)
|
|
{
|
|
case 0:
|
|
for (i = 0; i < numPlayers; i++)
|
|
ClearWindowTilemap(sPokemonJumpGfx->nameWindowIds[i]);
|
|
|
|
CopyBgTilemapBufferToVram(BG_INTERFACE);
|
|
sPokemonJumpGfx->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
for (i = 0; i < numPlayers; i++)
|
|
RemoveWindow(sPokemonJumpGfx->nameWindowIds[i]);
|
|
|
|
sPokemonJumpGfx->funcFinished = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void Msg_WantToPlayAgain(void)
|
|
{
|
|
switch (sPokemonJumpGfx->mainState)
|
|
{
|
|
case 0:
|
|
sPokemonJumpGfx->msgWindowId = AddMessageWindow(1, 8, 20, 2);
|
|
AddTextPrinterParameterized(sPokemonJumpGfx->msgWindowId, 1, gText_WantToPlayAgain2, 0, 1, TEXT_SPEED_FF, NULL);
|
|
CopyWindowToVram(sPokemonJumpGfx->msgWindowId, 2);
|
|
sPokemonJumpGfx->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
PutWindowTilemap(sPokemonJumpGfx->msgWindowId);
|
|
DrawTextBorderOuter(sPokemonJumpGfx->msgWindowId, 1, 14);
|
|
CreatePokeJumpYesNoMenu(23, 7, 0);
|
|
CopyBgTilemapBufferToVram(BG_INTERFACE);
|
|
sPokemonJumpGfx->mainState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
sPokemonJumpGfx->funcFinished = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void Msg_SavingDontTurnOff(void)
|
|
{
|
|
switch (sPokemonJumpGfx->mainState)
|
|
{
|
|
case 0:
|
|
sPokemonJumpGfx->msgWindowId = AddMessageWindow(2, 7, 26, 4);
|
|
AddTextPrinterParameterized(sPokemonJumpGfx->msgWindowId, 1, gText_SavingDontTurnOffPower, 0, 1, TEXT_SPEED_FF, NULL);
|
|
CopyWindowToVram(sPokemonJumpGfx->msgWindowId, 2);
|
|
sPokemonJumpGfx->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
PutWindowTilemap(sPokemonJumpGfx->msgWindowId);
|
|
DrawTextBorderOuter(sPokemonJumpGfx->msgWindowId, 1, 14);
|
|
CopyBgTilemapBufferToVram(BG_INTERFACE);
|
|
sPokemonJumpGfx->mainState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
sPokemonJumpGfx->funcFinished = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void EraseMessage(void)
|
|
{
|
|
switch (sPokemonJumpGfx->mainState)
|
|
{
|
|
case 0:
|
|
ClearMessageWindow();
|
|
sub_8198C78();
|
|
CopyBgTilemapBufferToVram(BG_INTERFACE);
|
|
sPokemonJumpGfx->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!RemoveMessageWindow() && !IsDma3ManagerBusyWithBgCopy())
|
|
sPokemonJumpGfx->funcFinished = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void Msg_SomeoneDroppedOut(void)
|
|
{
|
|
switch (sPokemonJumpGfx->mainState)
|
|
{
|
|
case 0:
|
|
sPokemonJumpGfx->msgWindowId = AddMessageWindow(2, 8, 22, 4);
|
|
AddTextPrinterParameterized(sPokemonJumpGfx->msgWindowId, 1, gText_SomeoneDroppedOut2, 0, 1, TEXT_SPEED_FF, NULL);
|
|
CopyWindowToVram(sPokemonJumpGfx->msgWindowId, 2);
|
|
sPokemonJumpGfx->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
PutWindowTilemap(sPokemonJumpGfx->msgWindowId);
|
|
DrawTextBorderOuter(sPokemonJumpGfx->msgWindowId, 1, 14);
|
|
CopyBgTilemapBufferToVram(BG_INTERFACE);
|
|
sPokemonJumpGfx->mainState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
sPokemonJumpGfx->funcFinished = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void Msg_CommunicationStandby(void)
|
|
{
|
|
switch (sPokemonJumpGfx->mainState)
|
|
{
|
|
case 0:
|
|
sPokemonJumpGfx->msgWindowId = AddMessageWindow(7, 10, 16, 2);
|
|
AddTextPrinterParameterized(sPokemonJumpGfx->msgWindowId, 1, gText_CommunicationStandby4, 0, 1, TEXT_SPEED_FF, NULL);
|
|
CopyWindowToVram(sPokemonJumpGfx->msgWindowId, 2);
|
|
sPokemonJumpGfx->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
PutWindowTilemap(sPokemonJumpGfx->msgWindowId);
|
|
DrawTextBorderOuter(sPokemonJumpGfx->msgWindowId, 1, 14);
|
|
CopyBgTilemapBufferToVram(BG_INTERFACE);
|
|
sPokemonJumpGfx->mainState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
sPokemonJumpGfx->funcFinished = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void DoPokeJumpCountdown(void)
|
|
{
|
|
switch (sPokemonJumpGfx->mainState)
|
|
{
|
|
case 0:
|
|
StartPokeJumpCountdown(sPokemonJumpGfx);
|
|
sPokemonJumpGfx->mainState++;
|
|
break;
|
|
case 1:
|
|
if (!IsPokeJumpCountdownRunning())
|
|
sPokemonJumpGfx->funcFinished = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void SetUpResetVineGfx(void)
|
|
{
|
|
sPokemonJumpGfx->resetVineState = 0;
|
|
sPokemonJumpGfx->resetVineTimer = 0;
|
|
sPokemonJumpGfx->vineState = VINE_UPSWING_LOWER;
|
|
UpdateVineSwing(sPokemonJumpGfx->vineState);
|
|
}
|
|
|
|
static bool32 ResetVineGfx(void)
|
|
{
|
|
switch (sPokemonJumpGfx->resetVineState)
|
|
{
|
|
case 0:
|
|
sPokemonJumpGfx->resetVineTimer++;
|
|
if (sPokemonJumpGfx->resetVineTimer > 10)
|
|
{
|
|
sPokemonJumpGfx->resetVineTimer = 0;
|
|
sPokemonJumpGfx->vineState++;
|
|
if (sPokemonJumpGfx->vineState >= NUM_VINESTATES)
|
|
{
|
|
sPokemonJumpGfx->vineState = VINE_HIGHEST;
|
|
sPokemonJumpGfx->resetVineState++;
|
|
}
|
|
}
|
|
UpdateVineSwing(sPokemonJumpGfx->vineState);
|
|
if (sPokemonJumpGfx->vineState != VINE_UPSWING_LOW)
|
|
break;
|
|
case 1:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void PrintPrizeMessage(u16 itemId, u16 quantity)
|
|
{
|
|
CopyItemNameHandlePlural(itemId, sPokemonJumpGfx->itemName, quantity);
|
|
ConvertIntToDecimalStringN(sPokemonJumpGfx->itemQuantityStr, quantity, STR_CONV_MODE_LEFT_ALIGN, 1);
|
|
DynamicPlaceholderTextUtil_Reset();
|
|
DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, sPokemonJumpGfx->itemName);
|
|
DynamicPlaceholderTextUtil_SetPlaceholderPtr(1, sPokemonJumpGfx->itemQuantityStr);
|
|
DynamicPlaceholderTextUtil_ExpandPlaceholders(sPokemonJumpGfx->prizeMsg, gText_AwesomeWonF701F700);
|
|
sPokemonJumpGfx->msgWindowId = AddMessageWindow(4, 8, 22, 4);
|
|
AddTextPrinterParameterized(sPokemonJumpGfx->msgWindowId, 1, sPokemonJumpGfx->prizeMsg, 0, 1, TEXT_SPEED_FF, NULL);
|
|
CopyWindowToVram(sPokemonJumpGfx->msgWindowId, 2);
|
|
sPokemonJumpGfx->fanfare = MUS_LEVEL_UP;
|
|
sPokemonJumpGfx->msgWindowState = 0;
|
|
}
|
|
|
|
static void PrintPrizeFilledBagMessage(u16 itemId)
|
|
{
|
|
CopyItemName(itemId, sPokemonJumpGfx->itemName);
|
|
DynamicPlaceholderTextUtil_Reset();
|
|
DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, sPokemonJumpGfx->itemName);
|
|
DynamicPlaceholderTextUtil_ExpandPlaceholders(sPokemonJumpGfx->prizeMsg, gText_FilledStorageSpace2);
|
|
sPokemonJumpGfx->msgWindowId = AddMessageWindow(4, 8, 22, 4);
|
|
AddTextPrinterParameterized(sPokemonJumpGfx->msgWindowId, 1, sPokemonJumpGfx->prizeMsg, 0, 1, TEXT_SPEED_FF, NULL);
|
|
CopyWindowToVram(sPokemonJumpGfx->msgWindowId, 2);
|
|
sPokemonJumpGfx->fanfare = MUS_DUMMY;
|
|
sPokemonJumpGfx->msgWindowState = 0;
|
|
}
|
|
|
|
static void PrintNoRoomForPrizeMessage(u16 itemId)
|
|
{
|
|
CopyItemName(itemId, sPokemonJumpGfx->itemName);
|
|
DynamicPlaceholderTextUtil_Reset();
|
|
DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, sPokemonJumpGfx->itemName);
|
|
DynamicPlaceholderTextUtil_ExpandPlaceholders(sPokemonJumpGfx->prizeMsg, gText_CantHoldMore);
|
|
sPokemonJumpGfx->msgWindowId = AddMessageWindow(4, 9, 22, 2);
|
|
AddTextPrinterParameterized(sPokemonJumpGfx->msgWindowId, 1, sPokemonJumpGfx->prizeMsg, 0, 1, TEXT_SPEED_FF, NULL);
|
|
CopyWindowToVram(sPokemonJumpGfx->msgWindowId, 2);
|
|
sPokemonJumpGfx->fanfare = MUS_DUMMY;
|
|
sPokemonJumpGfx->msgWindowState = 0;
|
|
}
|
|
|
|
static bool32 DoPrizeMessageAndFanfare(void)
|
|
{
|
|
switch (sPokemonJumpGfx->msgWindowState)
|
|
{
|
|
case 0:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
PutWindowTilemap(sPokemonJumpGfx->msgWindowId);
|
|
DrawTextBorderOuter(sPokemonJumpGfx->msgWindowId, 1, 14);
|
|
CopyBgTilemapBufferToVram(BG_INTERFACE);
|
|
sPokemonJumpGfx->msgWindowState++;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (IsDma3ManagerBusyWithBgCopy())
|
|
break;
|
|
if (sPokemonJumpGfx->fanfare == MUS_DUMMY)
|
|
{
|
|
sPokemonJumpGfx->msgWindowState += 2;
|
|
return FALSE;
|
|
}
|
|
PlayFanfare(sPokemonJumpGfx->fanfare);
|
|
sPokemonJumpGfx->msgWindowState++;
|
|
case 2:
|
|
if (!IsFanfareTaskInactive())
|
|
break;
|
|
sPokemonJumpGfx->msgWindowState++;
|
|
case 3:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void ClearMessageWindow(void)
|
|
{
|
|
if (sPokemonJumpGfx->msgWindowId != WINDOW_NONE)
|
|
{
|
|
rbox_fill_rectangle(sPokemonJumpGfx->msgWindowId);
|
|
CopyWindowToVram(sPokemonJumpGfx->msgWindowId, 1);
|
|
sPokemonJumpGfx->msgWindowState = 0;
|
|
}
|
|
}
|
|
|
|
static bool32 RemoveMessageWindow(void)
|
|
{
|
|
if (sPokemonJumpGfx->msgWindowId == WINDOW_NONE)
|
|
return FALSE;
|
|
|
|
switch (sPokemonJumpGfx->msgWindowState)
|
|
{
|
|
case 0:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
RemoveWindow(sPokemonJumpGfx->msgWindowId);
|
|
sPokemonJumpGfx->msgWindowId = WINDOW_NONE;
|
|
sPokemonJumpGfx->msgWindowState++;
|
|
}
|
|
else
|
|
break;
|
|
case 1:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static s8 HandlePlayAgainInput(void)
|
|
{
|
|
return Menu_ProcessInputNoWrapClearOnChoose();
|
|
}
|
|
|
|
static u32 AddMessageWindow(u32 left, u32 top, u32 width, u32 height)
|
|
{
|
|
u32 windowId;
|
|
struct WindowTemplate window;
|
|
|
|
window.bg = BG_INTERFACE;
|
|
window.tilemapLeft = left;
|
|
window.tilemapTop = top;
|
|
window.width = width;
|
|
window.height = height;
|
|
window.paletteNum = 0xF;
|
|
window.baseBlock = 0x43;
|
|
|
|
windowId = AddWindow(&window);
|
|
FillWindowPixelBuffer(windowId, 0x11);
|
|
return windowId;
|
|
}
|
|
|
|
static void CreatePokeJumpYesNoMenu(u16 left, u16 top, u8 cursorPos)
|
|
{
|
|
struct WindowTemplate window;
|
|
u8 a = cursorPos;
|
|
|
|
window.bg = BG_INTERFACE;
|
|
window.tilemapLeft = left;
|
|
window.tilemapTop = top;
|
|
window.width = 6;
|
|
window.height = 4;
|
|
window.paletteNum = 2;
|
|
window.baseBlock = 0x2B;
|
|
|
|
CreateYesNoMenu(&window, 1, 0xD, a);
|
|
}
|
|
|
|
// "Points" for jump score and "times" for number of jumps in a row
|
|
static void PrintScoreSuffixes(void)
|
|
{
|
|
u8 color[] = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY};
|
|
|
|
PutWindowTilemap(WIN_POINTS);
|
|
PutWindowTilemap(WIN_TIMES);
|
|
FillWindowPixelBuffer(WIN_POINTS, 0);
|
|
FillWindowPixelBuffer(WIN_TIMES, 0);
|
|
AddTextPrinterParameterized3(WIN_POINTS, 0, 0, 1, color, 0, gText_SpacePoints2);
|
|
AddTextPrinterParameterized3(WIN_TIMES, 0, 0, 1, color, 0, gText_SpaceTimes3);
|
|
}
|
|
|
|
// The venusaurs in the background are actually an empty 256x512 bg with 3 pairs of venusaurs on it.
|
|
// The below array is used to get values for where to set the bg Y to show the corresponding
|
|
// venusaur pair in their state of swinging the vine rope
|
|
// NEUTRAL/DOWN/UP refers to which direction the Venusaur is facing as it swings the vine
|
|
enum {
|
|
VENUSAUR_NEUTRAL,
|
|
VENUSAUR_DOWN,
|
|
VENUSAUR_UP,
|
|
};
|
|
|
|
static const u8 sVenusaurStates[] = {
|
|
[VINE_HIGHEST] = VENUSAUR_UP,
|
|
[VINE_DOWNSWING_HIGHER] = VENUSAUR_UP,
|
|
[VINE_DOWNSWING_HIGH] = VENUSAUR_NEUTRAL,
|
|
[VINE_DOWNSWING_LOW] = VENUSAUR_NEUTRAL,
|
|
[VINE_DOWNSWING_LOWER] = VENUSAUR_DOWN,
|
|
[VINE_LOWEST] = VENUSAUR_DOWN,
|
|
[VINE_UPSWING_LOWER] = VENUSAUR_DOWN,
|
|
[VINE_UPSWING_LOW] = VENUSAUR_NEUTRAL,
|
|
[VINE_UPSWING_HIGH] = VENUSAUR_NEUTRAL,
|
|
[VINE_UPSWING_HIGHER] = VENUSAUR_UP,
|
|
};
|
|
|
|
static const struct CompressedSpriteSheet sSpriteSheet_Digits = {gMinigameDigits_Gfx, 0, TAG_DIGITS};
|
|
static const struct SpritePalette sSpritePalette_Digits = {gMinigameDigits_Pal, TAG_DIGITS};
|
|
|
|
static const u16 sPlayerNameWindowCoords_2Players[] = {
|
|
6, 8,
|
|
16, 8
|
|
};
|
|
static const u16 sPlayerNameWindowCoords_3Players[] = {
|
|
6, 8,
|
|
11, 6,
|
|
16, 8
|
|
};
|
|
static const u16 sPlayerNameWindowCoords_4Players[] = {
|
|
2, 6,
|
|
6, 8,
|
|
16, 8,
|
|
20, 6
|
|
};
|
|
static const u16 sPlayerNameWindowCoords_5Players[] = {
|
|
2, 6,
|
|
6, 8,
|
|
11, 6,
|
|
16, 8,
|
|
20, 6
|
|
};
|
|
|
|
static const u16 *const sPlayerNameWindowCoords[MAX_RFU_PLAYERS - 1] =
|
|
{
|
|
sPlayerNameWindowCoords_2Players,
|
|
sPlayerNameWindowCoords_3Players,
|
|
sPlayerNameWindowCoords_4Players,
|
|
sPlayerNameWindowCoords_5Players,
|
|
};
|
|
|
|
static const s16 sMonXCoords_2Players[] = {88, 152};
|
|
static const s16 sMonXCoords_3Players[] = {88, 120, 152};
|
|
static const s16 sMonXCoords_4Players[] = {56, 88, 152, 184};
|
|
static const s16 sMonXCoords_5Players[] = {56, 88, 120, 152, 184};
|
|
|
|
static const s16 *const sMonXCoords[MAX_RFU_PLAYERS - 1] =
|
|
{
|
|
sMonXCoords_2Players,
|
|
sMonXCoords_3Players,
|
|
sMonXCoords_4Players,
|
|
sMonXCoords_5Players,
|
|
};
|
|
|
|
static void CreateJumpMonSprites(void)
|
|
{
|
|
int i, y, playersCount = GetNumPokeJumpPlayers();
|
|
const s16 *xCoords = sMonXCoords[playersCount - 2];
|
|
|
|
for (i = 0; i < playersCount; i++)
|
|
{
|
|
struct PokemonJump_MonInfo *monInfo = GetMonInfoByMultiplayerId(i);
|
|
|
|
y = gMonFrontPicCoords[monInfo->species].y_offset;
|
|
CreateJumpMonSprite(sPokemonJumpGfx, monInfo, *xCoords, y + 112, i);
|
|
CreateStarSprite(sPokemonJumpGfx, *xCoords, 112, i);
|
|
xCoords++;
|
|
}
|
|
}
|
|
|
|
static void SetMonSpriteY(u32 id, s16 y)
|
|
{
|
|
sPokemonJumpGfx->monSprites[id]->y2 = y;
|
|
}
|
|
|
|
static void UpdateVineSwing(int vineState)
|
|
{
|
|
UpdateVineAnim(sPokemonJumpGfx, vineState);
|
|
ChangeBgY(BG_VENUSAUR, (sVenusaurStates[vineState] * 5) << 13, 0);
|
|
}
|
|
|
|
static int DoSameJumpTimeBonus(u8 flags)
|
|
{
|
|
int i, numPlayers;
|
|
|
|
for (i = 0, numPlayers = 0; i < MAX_RFU_PLAYERS; i++)
|
|
{
|
|
if (flags & 1)
|
|
{
|
|
// Player was part of a synchronous jump
|
|
// Give a bonus to them
|
|
DoStarAnim(sPokemonJumpGfx, i);
|
|
numPlayers++;
|
|
}
|
|
flags >>= 1;
|
|
}
|
|
|
|
ShowBonus(numPlayers - 2);
|
|
return numPlayers;
|
|
}
|
|
|
|
static void InitDigitPrinters(void)
|
|
{
|
|
struct DigitObjUtilTemplate template = {
|
|
.shape = SPRITE_SHAPE(8x8),
|
|
.size = SPRITE_SIZE(8x8),
|
|
.strConvMode = 0,
|
|
.priority = 1,
|
|
.oamCount = 5,
|
|
.xDelta = 8,
|
|
.x = 108,
|
|
.y = 6,
|
|
.spriteSheet = (void*) &sSpriteSheet_Digits,
|
|
.spritePal = &sSpritePalette_Digits,
|
|
};
|
|
|
|
DigitObjUtil_Init(NUM_WINDOWS);
|
|
DigitObjUtil_CreatePrinter(WIN_POINTS, 0, &template);
|
|
|
|
template.oamCount = 4;
|
|
template.x = 30;
|
|
template.y = 6;
|
|
DigitObjUtil_CreatePrinter(WIN_TIMES, 0, &template);
|
|
}
|
|
|
|
static void PrintScore(int num)
|
|
{
|
|
DigitObjUtil_PrintNumOn(WIN_POINTS, num);
|
|
}
|
|
|
|
static void PrintJumpsInRow(u16 num)
|
|
{
|
|
DigitObjUtil_PrintNumOn(WIN_TIMES, num);
|
|
}
|
|
|
|
static void StartMonHitShake(u8 multiplayerId)
|
|
{
|
|
Gfx_StartMonHitShake(sPokemonJumpGfx, multiplayerId);
|
|
}
|
|
|
|
static void StartMonHitFlash(u8 multiplayerId)
|
|
{
|
|
Gfx_StartMonHitFlash(sPokemonJumpGfx, multiplayerId);
|
|
}
|
|
|
|
static int IsMonHitShakeActive(int multiplayerId)
|
|
{
|
|
return Gfx_IsMonHitShakeActive(sPokemonJumpGfx, multiplayerId);
|
|
}
|
|
|
|
static void StopMonHitFlash(void)
|
|
{
|
|
Gfx_StopMonHitFlash(sPokemonJumpGfx);
|
|
}
|
|
|
|
static void ResetMonSpriteSubpriorities(void)
|
|
{
|
|
Gfx_ResetMonSpriteSubpriorities(sPokemonJumpGfx);
|
|
}
|
|
|
|
static void StartMonIntroBounce(int multiplayerId)
|
|
{
|
|
Gfx_StartMonIntroBounce(sPokemonJumpGfx, multiplayerId);
|
|
}
|
|
|
|
static int IsMonIntroBounceActive(void)
|
|
{
|
|
return Gfx_IsMonIntroBounceActive(sPokemonJumpGfx);
|
|
}
|
|
|
|
static void AddPlayerNameWindows(void)
|
|
{
|
|
struct WindowTemplate window;
|
|
int i, playersCount = GetNumPokeJumpPlayers();
|
|
const u16 *winCoords = sPlayerNameWindowCoords[playersCount - 2];
|
|
|
|
window.bg = BG_INTERFACE;
|
|
window.width = 8;
|
|
window.height = 2;
|
|
window.paletteNum = 2;
|
|
window.baseBlock = 0x2B;
|
|
|
|
for (i = 0; i < playersCount; i++)
|
|
{
|
|
window.tilemapLeft = winCoords[0];
|
|
window.tilemapTop = winCoords[1];
|
|
sPokemonJumpGfx->nameWindowIds[i] = AddWindow(&window);
|
|
ClearWindowTilemap(sPokemonJumpGfx->nameWindowIds[i]);
|
|
window.baseBlock += 0x10;
|
|
winCoords += 2;
|
|
}
|
|
|
|
CopyBgTilemapBufferToVram(BG_INTERFACE);
|
|
}
|
|
|
|
static void PrintPokeJumpPlayerName(int multiplayerId, u8 bgColor, u8 fgColor, u8 shadow)
|
|
{
|
|
u32 x;
|
|
u8 colors[3] = {bgColor, fgColor, shadow};
|
|
|
|
FillWindowPixelBuffer(sPokemonJumpGfx->nameWindowIds[multiplayerId], 0);
|
|
x = 64 - GetStringWidth(1, GetPokeJumpPlayerName(multiplayerId), -1);
|
|
x /= 2;
|
|
AddTextPrinterParameterized3(sPokemonJumpGfx->nameWindowIds[multiplayerId], 1, x, 1, colors, -1, GetPokeJumpPlayerName(multiplayerId));
|
|
CopyWindowToVram(sPokemonJumpGfx->nameWindowIds[multiplayerId], 2);
|
|
}
|
|
|
|
static void PrintPokeJumpPlayerNames(bool32 highlightSelf)
|
|
{
|
|
int i, multiplayerId, playersCount = GetNumPokeJumpPlayers();
|
|
|
|
if (!highlightSelf)
|
|
{
|
|
for (i = 0; i < playersCount; i++)
|
|
PrintPokeJumpPlayerName(i, TEXT_COLOR_TRANSPARENT, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY);
|
|
}
|
|
else
|
|
{
|
|
// Highlight own name
|
|
multiplayerId = GetPokeJumpMultiplayerId();
|
|
for (i = 0; i < playersCount; i++)
|
|
{
|
|
if (multiplayerId != i)
|
|
PrintPokeJumpPlayerName(i, TEXT_COLOR_TRANSPARENT, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY);
|
|
else
|
|
PrintPokeJumpPlayerName(i, TEXT_COLOR_TRANSPARENT, TEXT_COLOR_RED, TEXT_COLOR_LIGHT_RED);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void DrawPlayerNameWindows(void)
|
|
{
|
|
int i, playersCount = GetNumPokeJumpPlayers();
|
|
|
|
for (i = 0; i < playersCount; i++)
|
|
PutWindowTilemap(sPokemonJumpGfx->nameWindowIds[i]);
|
|
CopyBgTilemapBufferToVram(BG_INTERFACE);
|
|
}
|
|
|
|
static void ShowBonus(u8 bonusId)
|
|
{
|
|
sPokemonJumpGfx->bonusTimer = 0;
|
|
ChangeBgX(BG_BONUSES, (bonusId / 2) * 256 * 256, 0);
|
|
ChangeBgY(BG_BONUSES, (((bonusId % 2) * 256) - 40) * 256, 0);
|
|
ShowBg(BG_BONUSES);
|
|
CreateTask(Task_UpdateBonus, 4);
|
|
}
|
|
|
|
static bool32 UpdateBonus(void)
|
|
{
|
|
if (sPokemonJumpGfx->bonusTimer >= 32)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ChangeBgY(BG_BONUSES, 128, 1);
|
|
if (++sPokemonJumpGfx->bonusTimer >= 32)
|
|
HideBg(BG_BONUSES);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static void Task_UpdateBonus(u8 taskId)
|
|
{
|
|
if (!UpdateBonus())
|
|
DestroyTask(taskId);
|
|
}
|
|
|
|
struct MonInfoPacket
|
|
{
|
|
u8 id;
|
|
u16 species;
|
|
u32 personality;
|
|
u32 otId;
|
|
};
|
|
|
|
static void SendPacket_MonInfo(struct PokemonJump_MonInfo *monInfo)
|
|
{
|
|
struct MonInfoPacket packet;
|
|
packet.id = PACKET_MON_INFO,
|
|
packet.species = monInfo->species,
|
|
packet.otId = monInfo->otId,
|
|
packet.personality = monInfo->personality,
|
|
Rfu_SendPacket(&packet);
|
|
}
|
|
|
|
static bool32 RecvPacket_MonInfo(int multiplayerId, struct PokemonJump_MonInfo *monInfo)
|
|
{
|
|
struct MonInfoPacket packet;
|
|
|
|
if ((gRecvCmds[multiplayerId][0] & 0xFF00) != RFUCMD_SEND_PACKET)
|
|
return FALSE;
|
|
|
|
memcpy(&packet, &gRecvCmds[multiplayerId][1], sizeof(packet));
|
|
if (packet.id == PACKET_MON_INFO)
|
|
{
|
|
monInfo->species = packet.species;
|
|
monInfo->otId = packet.otId;
|
|
monInfo->personality = packet.personality;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
struct UnusedPacket
|
|
{
|
|
u8 id;
|
|
u32 data;
|
|
u32 filler;
|
|
};
|
|
|
|
// Data packet that's never sent
|
|
// No function to read it either
|
|
static void SendPacket_Unused(u32 data)
|
|
{
|
|
struct UnusedPacket packet;
|
|
packet.id = PACKET_UNUSED;
|
|
packet.data = data;
|
|
Rfu_SendPacket(&packet);
|
|
}
|
|
|
|
struct LeaderStatePacket
|
|
{
|
|
u8 id;
|
|
u8 funcId;
|
|
u8 monState;
|
|
u8 receivedBonusFlags:5; // 1 bit for each player (MAX_RFU_PLAYERS)
|
|
u8 jumpState:3;
|
|
u16 jumpTimeStart;
|
|
u16 vineTimer;
|
|
u32 jumpsInRow:15;
|
|
u32 jumpScore:17;
|
|
};
|
|
|
|
static void SendPacket_LeaderState(struct PokemonJump_Player *player, struct PokemonJump_CommData *comm)
|
|
{
|
|
struct LeaderStatePacket packet;
|
|
packet.id = PACKET_LEADER_STATE;
|
|
packet.jumpScore = comm->jumpScore;
|
|
packet.receivedBonusFlags = comm->receivedBonusFlags;
|
|
packet.funcId = comm->funcId;
|
|
packet.vineTimer = comm->data;
|
|
packet.jumpsInRow = comm->jumpsInRow;
|
|
packet.monState = player->monState;
|
|
packet.jumpState = player->jumpState;
|
|
packet.jumpTimeStart = player->jumpTimeStart;
|
|
Rfu_SendPacket(&packet);
|
|
}
|
|
|
|
// Used by group members to read the state of the group leader
|
|
static bool32 RecvPacket_LeaderState(struct PokemonJump_Player *player, struct PokemonJump_CommData *comm)
|
|
{
|
|
struct LeaderStatePacket packet;
|
|
|
|
if ((gRecvCmds[0][0] & 0xFF00) != RFUCMD_SEND_PACKET)
|
|
return FALSE;
|
|
|
|
memcpy(&packet, &gRecvCmds[0][1], sizeof(packet));
|
|
if (packet.id != PACKET_LEADER_STATE)
|
|
return FALSE;
|
|
|
|
comm->jumpScore = packet.jumpScore;
|
|
comm->receivedBonusFlags = packet.receivedBonusFlags;
|
|
comm->funcId = packet.funcId;
|
|
comm->data = packet.vineTimer;
|
|
comm->jumpsInRow = packet.jumpsInRow;
|
|
player->monState = packet.monState;
|
|
player->jumpState = packet.jumpState;
|
|
player->jumpTimeStart = packet.jumpTimeStart;
|
|
return TRUE;
|
|
}
|
|
|
|
struct MemberStatePacket
|
|
{
|
|
u8 id;
|
|
u8 monState;
|
|
u8 jumpState;
|
|
bool8 funcFinished;
|
|
u16 jumpTimeStart;
|
|
u8 funcId;
|
|
u16 playAgainState;
|
|
};
|
|
|
|
static void SendPacket_MemberState(struct PokemonJump_Player *player, u8 funcId, u16 playAgainState)
|
|
{
|
|
struct MemberStatePacket packet;
|
|
packet.id = PACKET_MEMBER_STATE;
|
|
packet.monState = player->monState;
|
|
packet.jumpState = player->jumpState;
|
|
packet.funcFinished = player->funcFinished;
|
|
packet.jumpTimeStart = player->jumpTimeStart;
|
|
packet.funcId = funcId;
|
|
packet.playAgainState = playAgainState;
|
|
Rfu_SendPacket(&packet);
|
|
}
|
|
|
|
// Used by the group leader to read the state of group members
|
|
static bool32 RecvPacket_MemberStateToLeader(struct PokemonJump_Player *player, int multiplayerId, u8 *funcId, u16 *playAgainState)
|
|
{
|
|
struct MemberStatePacket packet;
|
|
|
|
if ((gRecvCmds[multiplayerId][0] & 0xFF00) != RFUCMD_SEND_PACKET)
|
|
return FALSE;
|
|
|
|
memcpy(&packet, &gRecvCmds[multiplayerId][1], sizeof(packet));
|
|
if (packet.id != PACKET_MEMBER_STATE)
|
|
return FALSE;
|
|
|
|
player->monState = packet.monState;
|
|
player->jumpState = packet.jumpState;
|
|
player->funcFinished = packet.funcFinished;
|
|
player->jumpTimeStart = packet.jumpTimeStart;
|
|
*funcId = packet.funcId;
|
|
*playAgainState = packet.playAgainState;
|
|
return TRUE;
|
|
}
|
|
|
|
// Used by group members to read the state of other group members
|
|
static bool32 RecvPacket_MemberStateToMember(struct PokemonJump_Player *player, int multiplayerId)
|
|
{
|
|
struct MemberStatePacket packet;
|
|
|
|
if ((gRecvCmds[multiplayerId][0] & 0xFF00) != RFUCMD_SEND_PACKET)
|
|
return FALSE;
|
|
|
|
memcpy(&packet, &gRecvCmds[multiplayerId][1], sizeof(packet));
|
|
if (packet.id != PACKET_MEMBER_STATE)
|
|
return FALSE;
|
|
|
|
player->monState = packet.monState;
|
|
player->jumpState = packet.jumpState;
|
|
player->funcFinished = packet.funcFinished;
|
|
player->jumpTimeStart = packet.jumpTimeStart;
|
|
return TRUE;
|
|
}
|
|
|
|
static struct PokemonJumpRecords *GetPokeJumpRecords(void)
|
|
{
|
|
return &gSaveBlock2Ptr->pokeJump;
|
|
}
|
|
|
|
void ResetPokemonJumpRecords(void)
|
|
{
|
|
struct PokemonJumpRecords *records = GetPokeJumpRecords();
|
|
records->jumpsInRow = 0;
|
|
records->bestJumpScore = 0;
|
|
records->excellentsInRow = 0;
|
|
records->gamesWithMaxPlayers = 0;
|
|
records->unused2 = 0;
|
|
records->unused1 = 0;
|
|
}
|
|
|
|
static bool32 TryUpdateRecords(u32 jumpScore, u16 jumpsInRow, u16 excellentsInRow)
|
|
{
|
|
struct PokemonJumpRecords *records = GetPokeJumpRecords();
|
|
bool32 newRecord = FALSE;
|
|
|
|
if (records->bestJumpScore < jumpScore && jumpScore <= MAX_JUMP_SCORE)
|
|
records->bestJumpScore = jumpScore, newRecord = TRUE;
|
|
if (records->jumpsInRow < jumpsInRow && jumpsInRow <= MAX_JUMPS)
|
|
records->jumpsInRow = jumpsInRow, newRecord = TRUE;
|
|
if (records->excellentsInRow < excellentsInRow && excellentsInRow <= MAX_JUMPS)
|
|
records->excellentsInRow = excellentsInRow, newRecord = TRUE;
|
|
|
|
return newRecord;
|
|
}
|
|
|
|
static void IncrementGamesWithMaxPlayers(void)
|
|
{
|
|
struct PokemonJumpRecords *records = GetPokeJumpRecords();
|
|
if (records->gamesWithMaxPlayers < 9999)
|
|
records->gamesWithMaxPlayers++;
|
|
}
|
|
|
|
void ShowPokemonJumpRecords(void)
|
|
{
|
|
u8 taskId = CreateTask(Task_ShowPokemonJumpRecords, 0);
|
|
Task_ShowPokemonJumpRecords(taskId);
|
|
}
|
|
|
|
static const struct WindowTemplate sWindowTemplate_Records =
|
|
{
|
|
.bg = 0,
|
|
.tilemapLeft = 1,
|
|
.tilemapTop = 1,
|
|
.width = 28,
|
|
.height = 9,
|
|
.paletteNum = 15,
|
|
.baseBlock = 0x1,
|
|
};
|
|
|
|
static const u8 *const sRecordsTexts[] = {gText_JumpsInARow, gText_BestScore2, gText_ExcellentsInARow};
|
|
|
|
#define tState data[0]
|
|
#define tWindowId data[1]
|
|
|
|
static void Task_ShowPokemonJumpRecords(u8 taskId)
|
|
{
|
|
struct WindowTemplate window;
|
|
int i, width, widthCurr;
|
|
s16 *data = gTasks[taskId].data;
|
|
|
|
switch (tState)
|
|
{
|
|
case 0:
|
|
window = sWindowTemplate_Records;
|
|
width = GetStringWidth(1, gText_PkmnJumpRecords, 0);
|
|
for (i = 0; i < ARRAY_COUNT(sRecordsTexts); i++)
|
|
{
|
|
widthCurr = GetStringWidth(1, sRecordsTexts[i], 0) + 38;
|
|
if (widthCurr > width)
|
|
width = widthCurr;
|
|
}
|
|
width = (width + 7) / 8;
|
|
if (width & 1)
|
|
width++;
|
|
window.tilemapLeft = (30 - width) / 2;
|
|
window.width = width;
|
|
tWindowId = AddWindow(&window);
|
|
PrintRecordsText(tWindowId, width);
|
|
CopyWindowToVram(tWindowId, 3);
|
|
tState++;
|
|
break;
|
|
case 1:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
tState++;
|
|
break;
|
|
case 2:
|
|
if (JOY_NEW(A_BUTTON | B_BUTTON))
|
|
{
|
|
rbox_fill_rectangle(tWindowId);
|
|
CopyWindowToVram(tWindowId, 1);
|
|
tState++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (!IsDma3ManagerBusyWithBgCopy())
|
|
{
|
|
RemoveWindow(tWindowId);
|
|
DestroyTask(taskId);
|
|
EnableBothScriptContexts();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#undef tState
|
|
#undef tWindowId
|
|
|
|
static void PrintRecordsText(u16 windowId, int width)
|
|
{
|
|
int i, x;
|
|
int recordNums[3];
|
|
struct PokemonJumpRecords *records = GetPokeJumpRecords();
|
|
recordNums[0] = records->jumpsInRow;
|
|
recordNums[1] = records->bestJumpScore;
|
|
recordNums[2] = records->excellentsInRow;
|
|
|
|
LoadUserWindowBorderGfx_(windowId, 0x21D, 0xD0);
|
|
DrawTextBorderOuter(windowId, 0x21D, 0xD);
|
|
FillWindowPixelBuffer(windowId, PIXEL_FILL(1));
|
|
AddTextPrinterParameterized(windowId, 1, gText_PkmnJumpRecords, GetStringCenterAlignXOffset(1, gText_PkmnJumpRecords, width * 8), 1, TEXT_SPEED_FF, NULL);
|
|
for (i = 0; i < ARRAY_COUNT(sRecordsTexts); i++)
|
|
{
|
|
AddTextPrinterParameterized(windowId, 1, sRecordsTexts[i], 0, 25 + (i * 16), TEXT_SPEED_FF, NULL);
|
|
ConvertIntToDecimalStringN(gStringVar1, recordNums[i], STR_CONV_MODE_LEFT_ALIGN, 5);
|
|
TruncateToFirstWordOnly(gStringVar1);
|
|
x = (width * 8) - GetStringWidth(1, gStringVar1, 0);
|
|
AddTextPrinterParameterized(windowId, 1, gStringVar1, x, 25 + (i * 16), TEXT_SPEED_FF, NULL);
|
|
}
|
|
PutWindowTilemap(windowId);
|
|
}
|
|
|
|
static void TruncateToFirstWordOnly(u8 *str)
|
|
{
|
|
for (;*str != EOS; str++)
|
|
{
|
|
if (*str == CHAR_SPACE)
|
|
{
|
|
*str = EOS;
|
|
break;
|
|
}
|
|
}
|
|
}
|