Battle Transition: mugshots for multibattles (#6567)

Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
This commit is contained in:
grintoul1 2025-04-17 15:18:20 +01:00 committed by GitHub
parent 85ecc91532
commit ec2aef2eff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 164 additions and 42 deletions

View File

@ -171,6 +171,7 @@ extern const union AnimCmd *const gAnims_MonPic[];
extern const union AnimCmd *const gAnims_Trainer[];
extern const struct TrainerSprite gTrainerSprites[];
extern const struct TrainerBacksprite gTrainerBacksprites[];
extern const u16 gTrainerPicToTrainerBackPic[];
extern const struct Trainer gTrainers[DIFFICULTY_COUNT][TRAINERS_COUNT];
extern const struct Trainer gBattlePartners[DIFFICULTY_COUNT][PARTNER_COUNT];
@ -239,6 +240,10 @@ static inline const u8 GetTrainerPicFromId(u16 trainerId)
{
u32 sanitizedTrainerId = SanitizeTrainerId(trainerId);
enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(sanitizedTrainerId);
enum DifficultyLevel partnerDifficulty = GetBattlePartnerDifficultyLevel(trainerId);
if (trainerId > TRAINER_PARTNER(PARTNER_NONE))
return gBattlePartners[partnerDifficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerPic;
return gTrainers[difficulty][sanitizedTrainerId].trainerPic;
}

View File

@ -613,8 +613,8 @@ extern bool32 consumeItem;
extern u32 removeBagItem;
extern u32 removeBagItemCount;
extern const u8 gFacilityClassToPicIndex[];
extern const u8 gFacilityClassToTrainerClass[];
extern const u16 gFacilityClassToPicIndex[];
extern const u16 gFacilityClassToTrainerClass[];
extern const struct SpeciesInfo gSpeciesInfo[];
extern const u32 gExperienceTables[][MAX_LEVEL + 1];
extern const u8 gPPUpGetMask[];

View File

@ -88,6 +88,7 @@ struct RectangularSpiralLine
typedef bool8 (*TransitionStateFunc)(struct Task *task);
typedef bool8 (*TransitionSpriteCallback)(struct Sprite *sprite);
typedef bool8 (*TransitionSpriteCallbackPartner)(struct Sprite *sprite);
static bool8 Transition_StartIntro(struct Task *);
static bool8 Transition_WaitForIntro(struct Task *);
@ -277,11 +278,13 @@ static bool8 IsIntroTaskDone(void);
static bool16 UpdateRectangularSpiralLine(const s16 * const *, struct RectangularSpiralLine *);
static void SpriteCB_FldEffPokeballTrail(struct Sprite *);
static void SpriteCB_MugshotTrainerPic(struct Sprite *);
static void SpriteCB_MugshotTrainerPicPartner(struct Sprite *);
static void SpriteCB_WhiteBarFade(struct Sprite *);
static bool8 MugshotTrainerPic_Pause(struct Sprite *);
static bool8 MugshotTrainerPic_Init(struct Sprite *);
static bool8 MugshotTrainerPic_Slide(struct Sprite *);
static bool8 MugshotTrainerPic_SlideSlow(struct Sprite *);
static bool8 MugshotTrainerPic_SlidePartner(struct Sprite *);
static bool8 MugshotTrainerPic_SlideOffscreen(struct Sprite *);
static s16 sDebug_RectangularSpiralData;
@ -544,6 +547,17 @@ static const TransitionSpriteCallback sMugshotTrainerPicFuncs[] =
MugshotTrainerPic_Pause
};
static const TransitionSpriteCallbackPartner sMugshotTrainerPicFuncsPartner[] =
{
MugshotTrainerPic_Pause,
MugshotTrainerPic_Init,
MugshotTrainerPic_SlidePartner,
MugshotTrainerPic_SlideSlow,
MugshotTrainerPic_Pause,
MugshotTrainerPic_SlideOffscreen,
MugshotTrainerPic_Pause
};
// One element per slide direction.
// Sign of acceleration is opposite speed, so slide decelerates.
static const s16 sTrainerPicSlideSpeeds[2] = {12, -12};
@ -2217,8 +2231,10 @@ static void VBlankCB_Wave(void)
#define tBottomBannerX data[3]
#define tTimer data[3] // Re-used
#define tFadeSpread data[4]
#define tOpponentSpriteId data[13]
#define tOpponentSpriteAId data[11]
#define tOpponentSpriteBId data[12]
#define tPlayerSpriteId data[14]
#define tPartnerSpriteId data[13]
// Sprite data for trainer sprites in mugshots
#define sState data[0]
@ -2359,14 +2375,18 @@ static bool8 Mugshot_StartOpponentSlide(struct Task *task)
sTransitionData->BG0HOFS_Lower -= 8;
sTransitionData->BG0HOFS_Upper += 8;
SetTrainerPicSlideDirection(task->tOpponentSpriteId, 0);
SetTrainerPicSlideDirection(task->tOpponentSpriteAId, 0);
SetTrainerPicSlideDirection(task->tOpponentSpriteBId, 0);
SetTrainerPicSlideDirection(task->tPlayerSpriteId, 1);
SetTrainerPicSlideDirection(task->tPartnerSpriteId, 1);
// Start opponent slide
IncrementTrainerPicState(task->tOpponentSpriteId);
IncrementTrainerPicState(task->tOpponentSpriteAId);
PlaySE(SE_MUGSHOT);
IncrementTrainerPicState(task->tOpponentSpriteBId);
sTransitionData->VBlank_DMA++;
return FALSE;
}
@ -2377,9 +2397,10 @@ static bool8 Mugshot_WaitStartPlayerSlide(struct Task *task)
sTransitionData->BG0HOFS_Upper += 8;
// Start player's slide in once the opponent is finished
if (IsTrainerPicSlideDone(task->tOpponentSpriteId))
if (IsTrainerPicSlideDone(task->tOpponentSpriteAId))
{
task->tState++;
IncrementTrainerPicState(task->tPartnerSpriteId);
IncrementTrainerPicState(task->tPlayerSpriteId);
}
return FALSE;
@ -2390,20 +2411,49 @@ static bool8 Mugshot_WaitPlayerSlide(struct Task *task)
sTransitionData->BG0HOFS_Lower -= 8;
sTransitionData->BG0HOFS_Upper += 8;
if (IsTrainerPicSlideDone(task->tPlayerSpriteId))
if (gPartnerTrainerId != TRAINER_PARTNER(PARTNER_NONE) && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
{
sTransitionData->VBlank_DMA = FALSE;
SetVBlankCallback(NULL);
DmaStop(0);
memset(gScanlineEffectRegBuffers[0], 0, DISPLAY_HEIGHT * 2);
memset(gScanlineEffectRegBuffers[1], 0, DISPLAY_HEIGHT * 2);
SetGpuReg(REG_OFFSET_WIN0H, DISPLAY_WIDTH);
SetGpuReg(REG_OFFSET_BLDY, 0);
task->tState++;
task->tTimer = 0;
task->tFadeSpread = 0;
sTransitionData->BLDCNT = BLDCNT_TGT1_ALL | BLDCNT_EFFECT_LIGHTEN;
SetVBlankCallback(VBlankCB_MugshotsFadeOut);
if (IsTrainerPicSlideDone(task->tPartnerSpriteId))
{
sTransitionData->VBlank_DMA = FALSE;
SetVBlankCallback(NULL);
DmaStop(0);
memset(gScanlineEffectRegBuffers[0], 0, DISPLAY_HEIGHT * 2);
memset(gScanlineEffectRegBuffers[1], 0, DISPLAY_HEIGHT * 2);
SetGpuReg(REG_OFFSET_WIN0H, DISPLAY_WIDTH);
SetGpuReg(REG_OFFSET_BLDY, 0);
task->tState++;
task->tTimer = 0;
task->tFadeSpread = 0;
sTransitionData->BLDCNT = BLDCNT_TGT1_ALL | BLDCNT_EFFECT_LIGHTEN;
SetVBlankCallback(VBlankCB_MugshotsFadeOut);
}
else
{
return FALSE;
}
}
else
{
if (IsTrainerPicSlideDone(task->tPlayerSpriteId))
{
sTransitionData->VBlank_DMA = FALSE;
SetVBlankCallback(NULL);
DmaStop(0);
memset(gScanlineEffectRegBuffers[0], 0, DISPLAY_HEIGHT * 2);
memset(gScanlineEffectRegBuffers[1], 0, DISPLAY_HEIGHT * 2);
SetGpuReg(REG_OFFSET_WIN0H, DISPLAY_WIDTH);
SetGpuReg(REG_OFFSET_BLDY, 0);
task->tState++;
task->tTimer = 0;
task->tFadeSpread = 0;
sTransitionData->BLDCNT = BLDCNT_TGT1_ALL | BLDCNT_EFFECT_LIGHTEN;
SetVBlankCallback(VBlankCB_MugshotsFadeOut);
}
else
{
return FALSE;
}
}
return FALSE;
}
@ -2518,48 +2568,83 @@ static void HBlankCB_Mugshots(void)
static void Mugshots_CreateTrainerPics(struct Task *task)
{
struct Sprite *opponentSprite, *playerSprite;
struct Sprite *opponentSpriteA, *opponentSpriteB=0, *playerSprite, *partnerSprite=0;
u8 trainerPicId = GetTrainerPicFromId(TRAINER_BATTLE_PARAM.opponentA);
s16 opponentRotationScales = 0;
u8 trainerAPicId = GetTrainerPicFromId(TRAINER_BATTLE_PARAM.opponentA);
u8 trainerBPicId = GetTrainerPicFromId(TRAINER_BATTLE_PARAM.opponentB);
u8 partnerPicId = gTrainerPicToTrainerBackPic[GetTrainerPicFromId(gPartnerTrainerId)];
s16 opponentARotationScales = 0;
s16 opponentBRotationScales = 0;
gReservedSpritePaletteCount = 10;
task->tOpponentSpriteId = CreateTrainerSprite(trainerPicId,
gTrainerSprites[trainerPicId].mugshotCoords.x - 32,
gTrainerSprites[trainerPicId].mugshotCoords.y + 42,
if (TRAINER_BATTLE_PARAM.opponentB != TRAINER_NONE && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
{
task->tOpponentSpriteBId = CreateTrainerSprite(trainerBPicId,
gTrainerSprites[trainerBPicId].mugshotCoords.x - 240,
gTrainerSprites[trainerBPicId].mugshotCoords.y + 42,
0, NULL);
opponentSpriteB = &gSprites[task->tOpponentSpriteBId];
opponentSpriteB->callback = SpriteCB_MugshotTrainerPicPartner;
opponentSpriteB->oam.affineMode = ST_OAM_AFFINE_DOUBLE;
opponentSpriteB->oam.matrixNum = AllocOamMatrix();
opponentSpriteB->oam.shape = SPRITE_SHAPE(64x32);
opponentSpriteB->oam.size = SPRITE_SIZE(64x32);
CalcCenterToCornerVec(opponentSpriteB, SPRITE_SHAPE(64x32), SPRITE_SIZE(64x32), ST_OAM_AFFINE_DOUBLE);
opponentBRotationScales = gTrainerSprites[trainerBPicId].mugshotRotation;
SetOamMatrixRotationScaling(opponentSpriteB->oam.matrixNum, opponentBRotationScales, opponentBRotationScales, 0);
}
task->tOpponentSpriteAId = CreateTrainerSprite(trainerAPicId,
gTrainerSprites[trainerAPicId].mugshotCoords.x - 32,
gTrainerSprites[trainerAPicId].mugshotCoords.y + 42,
0, NULL);
gReservedSpritePaletteCount = 12;
task->tPlayerSpriteId = CreateTrainerSprite(PlayerGenderToFrontTrainerPicId(gSaveBlock2Ptr->playerGender),
DISPLAY_WIDTH + 32,
106,
if (gPartnerTrainerId != TRAINER_PARTNER(PARTNER_NONE) && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
{
task->tPartnerSpriteId = CreateTrainerSprite(partnerPicId,
DISPLAY_WIDTH + 240,
106,
0, NULL);
partnerSprite = &gSprites[task->tPartnerSpriteId];
partnerSprite->callback = SpriteCB_MugshotTrainerPicPartner;
partnerSprite->oam.affineMode = ST_OAM_AFFINE_DOUBLE;
partnerSprite->oam.matrixNum = AllocOamMatrix();
partnerSprite->oam.shape = SPRITE_SHAPE(64x32);
partnerSprite->oam.size = SPRITE_SIZE(64x32);
CalcCenterToCornerVec(partnerSprite, SPRITE_SHAPE(64x32), SPRITE_SIZE(64x32), ST_OAM_AFFINE_DOUBLE);
SetOamMatrixRotationScaling(partnerSprite->oam.matrixNum, -512, 512, 0);
}
opponentSprite = &gSprites[task->tOpponentSpriteId];
task->tPlayerSpriteId = CreateTrainerSprite(PlayerGenderToFrontTrainerPicId(gSaveBlock2Ptr->playerGender),
DISPLAY_WIDTH + 32,
106,
0, NULL);
opponentSpriteA = &gSprites[task->tOpponentSpriteAId];
playerSprite = &gSprites[task->tPlayerSpriteId];
opponentSprite->callback = SpriteCB_MugshotTrainerPic;
opponentSpriteA->callback = SpriteCB_MugshotTrainerPic;
playerSprite->callback = SpriteCB_MugshotTrainerPic;
opponentSprite->oam.affineMode = ST_OAM_AFFINE_DOUBLE;
opponentSpriteA->oam.affineMode = ST_OAM_AFFINE_DOUBLE;
playerSprite->oam.affineMode = ST_OAM_AFFINE_DOUBLE;
opponentSprite->oam.matrixNum = AllocOamMatrix();
opponentSpriteA->oam.matrixNum = AllocOamMatrix();
playerSprite->oam.matrixNum = AllocOamMatrix();
opponentSprite->oam.shape = SPRITE_SHAPE(64x32);
opponentSpriteA->oam.shape = SPRITE_SHAPE(64x32);
playerSprite->oam.shape = SPRITE_SHAPE(64x32);
opponentSprite->oam.size = SPRITE_SIZE(64x32);
opponentSpriteA->oam.size = SPRITE_SIZE(64x32);
playerSprite->oam.size = SPRITE_SIZE(64x32);
CalcCenterToCornerVec(opponentSprite, SPRITE_SHAPE(64x32), SPRITE_SIZE(64x32), ST_OAM_AFFINE_DOUBLE);
CalcCenterToCornerVec(opponentSpriteA, SPRITE_SHAPE(64x32), SPRITE_SIZE(64x32), ST_OAM_AFFINE_DOUBLE);
CalcCenterToCornerVec(playerSprite, SPRITE_SHAPE(64x32), SPRITE_SIZE(64x32), ST_OAM_AFFINE_DOUBLE);
opponentRotationScales = gTrainerSprites[trainerPicId].mugshotRotation;
SetOamMatrixRotationScaling(opponentSprite->oam.matrixNum, opponentRotationScales, opponentRotationScales, 0);
opponentARotationScales = gTrainerSprites[trainerAPicId].mugshotRotation;
SetOamMatrixRotationScaling(opponentSpriteA->oam.matrixNum, opponentARotationScales, opponentARotationScales, 0);
SetOamMatrixRotationScaling(playerSprite->oam.matrixNum, -512, 512, 0);
}
@ -2568,6 +2653,12 @@ static void SpriteCB_MugshotTrainerPic(struct Sprite *sprite)
while (sMugshotTrainerPicFuncs[sprite->sState](sprite));
}
static void SpriteCB_MugshotTrainerPicPartner(struct Sprite *sprite)
{
while (sMugshotTrainerPicFuncsPartner[sprite->sState](sprite));
}
// Wait until IncrementTrainerPicState is called
static bool8 MugshotTrainerPic_Pause(struct Sprite *sprite)
{
@ -2600,6 +2691,18 @@ static bool8 MugshotTrainerPic_Slide(struct Sprite *sprite)
return FALSE;
}
static bool8 MugshotTrainerPic_SlidePartner(struct Sprite *sprite)
{
sprite->x += sprite->sSlideSpeed;
// Advance state when pic passes ~40% of screen
if (sprite->sSlideDir && sprite->x < DISPLAY_WIDTH - 60)
sprite->sState++;
else if (!sprite->sSlideDir && sprite->x > 60)
sprite->sState++;
return FALSE;
}
static bool8 MugshotTrainerPic_SlideSlow(struct Sprite *sprite)
{
// Add acceleration value to speed, then add speed.
@ -2654,8 +2757,10 @@ static s16 IsTrainerPicSlideDone(s16 spriteId)
#undef tBottomBannerX
#undef tTimer
#undef tFadeSpread
#undef tOpponentSpriteId
#undef tOpponentSpriteAId
#undef tOpponentSpriteBId
#undef tPlayerSpriteId
#undef tPartnerSpriteId
//--------------------
// B_TRANSITION_SLICE

View File

@ -1,4 +1,4 @@
const u8 gFacilityClassToPicIndex[] =
const u16 gFacilityClassToPicIndex[] =
{
[FACILITY_CLASS_HIKER] = TRAINER_PIC_HIKER,
[FACILITY_CLASS_AQUA_GRUNT_M] = TRAINER_PIC_AQUA_GRUNT_M,
@ -84,7 +84,19 @@ const u8 gFacilityClassToPicIndex[] =
[FACILITY_CLASS_RS_MAY] = TRAINER_PIC_RS_MAY,
};
const u8 gFacilityClassToTrainerClass[] =
const u16 gTrainerPicToTrainerBackPic[] =
{
[TRAINER_BACK_PIC_BRENDAN] = TRAINER_PIC_BRENDAN,
[TRAINER_BACK_PIC_MAY] = TRAINER_PIC_MAY,
[TRAINER_BACK_PIC_RED] = TRAINER_PIC_RED,
[TRAINER_BACK_PIC_LEAF] = TRAINER_PIC_LEAF,
[TRAINER_BACK_PIC_RUBY_SAPPHIRE_BRENDAN] = TRAINER_PIC_RS_BRENDAN,
[TRAINER_BACK_PIC_RUBY_SAPPHIRE_MAY] = TRAINER_PIC_RS_MAY,
[TRAINER_BACK_PIC_WALLY] = TRAINER_PIC_WALLY,
[TRAINER_BACK_PIC_STEVEN] = TRAINER_PIC_STEVEN,
};
const u16 gFacilityClassToTrainerClass[] =
{
[FACILITY_CLASS_HIKER] = TRAINER_CLASS_HIKER,
[FACILITY_CLASS_AQUA_GRUNT_M] = TRAINER_CLASS_TEAM_AQUA,