• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

[Graphics] [pokeemerald] Help needed: adding another animation after shiny animation

23
Posts
6
Years
    • Seen Nov 28, 2023
    Hi all!

    I am trying to add a feature where an animation is shown when a pokemon enters battle, similarly to the shiny animation. For demo purposes, the condition for this animation triggering is that the mon's species is Treecko. However, I've encountered a problem as the game is softlocked after showing the animation, so my guess is that some sort of return statement or callbacks should be involved, but I'm not skilled enough in C to debug this myself. Any help in making the battle resume normally after the animation would be immensely appreciated!

    Background: I am using RHH's pokeemerald-expansion version 1.6.1.

    The animation I've been using is the one for Harden and Iron defense from src/battle_anim_dark.c. I've copied and renamed the functions AnimTask_MetallicShine and AnimTask_MetallicShine_Step. I've inserted them into src/battle_anim_throw.c, where the important function TryShinyAnimation is.

    Changes in src/battle_anim_throw.c:

    added in the very beginning of the file:
    Code:
    #include "contest.h"


    added where functions are declared:
    Code:
    static void AnimTask_MetallicShineCopy(u8);
    static void AnimTask_MetallicShine_StepCopy(u8);


    changes in the function TryShinyAnimation:
    Code:
    void TryShinyAnimation(u8 battler, struct Pokemon *mon)
    {
        bool8 isShiny;
        u32 otId, personality;
        u32 shinyValue;
        u8 taskCirc, taskDgnl;
        struct Pokemon* illusionMon;
    
        // custom
        u8 taskMetallicShine;
        // end custom
    
        isShiny = FALSE;
        gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = TRUE;
        illusionMon = GetIllusionMonPtr(battler);
        if (illusionMon != NULL)
            mon = illusionMon;
    
        otId = GetMonData(mon, MON_DATA_OT_ID);
        personality = GetMonData(mon, MON_DATA_PERSONALITY);
    
        if (IsBattlerSpriteVisible(battler) && IsValidForBattle(mon))
        {
            shinyValue = GET_SHINY_VALUE(otId, personality);
            if (shinyValue < SHINY_ODDS)
                isShiny = TRUE;
    
            if (isShiny)
            {
                if (GetSpriteTileStartByTag(ANIM_TAG_GOLD_STARS) == 0xFFFF)
                {
                    LoadCompressedSpriteSheetUsingHeap(&gBattleAnimPicTable[ANIM_TAG_GOLD_STARS - ANIM_SPRITES_START]);
                    LoadCompressedSpritePaletteUsingHeap(&gBattleAnimPaletteTable[ANIM_TAG_GOLD_STARS - ANIM_SPRITES_START]);
                }
    
                taskCirc = CreateTask(Task_ShinyStars, 10);
                taskDgnl = CreateTask(Task_ShinyStars, 10);
                gTasks[taskCirc].tBattler = battler;
                gTasks[taskDgnl].tBattler = battler;
                gTasks[taskCirc].tStarMove = SHINY_STAR_ENCIRCLE;
                gTasks[taskDgnl].tStarMove = SHINY_STAR_DIAGONAL;
                return;
            }
    
            // custom
            if (GetMonData(mon, MON_DATA_SPECIES) == SPECIES_TREECKO)
            {
                taskMetallicShine = CreateTask(AnimTask_MetallicShineCopy, 10);
                gBattleAnimArgs[1] = TRUE;
                gBattleAnimArgs[2] = RGB(0,10,20);
                return;
            }
            // end custom
    
        }
    
        gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = TRUE;
    }


    inserted between functions SpriteCB_ShinyStars_Encircle and AnimTask_LoadPokeblockGfx:
    Code:
    #define tTimer     data[13]
    
    void AnimTask_MetallicShineCopy(u8 taskId)
    {
        u16 species;
        u8 spriteId;
        u8 newSpriteId;
        u16 paletteNum;
        struct BattleAnimBgData animBg;
        bool32 priorityChanged = FALSE;
    
        if (gTasks[taskId].tTimer < 60)
        {
            gTasks[taskId].tTimer++;
            return;
        }
    
        gBattle_WIN0H = 0;
        gBattle_WIN0V = 0;
        SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_BG_ALL | WININ_WIN0_OBJ | WININ_WIN0_CLR | WININ_WIN1_BG_ALL | WININ_WIN1_OBJ | WININ_WIN1_CLR);
        SetGpuReg(REG_OFFSET_WINOUT, WINOUT_WINOBJ_BG_ALL | WINOUT_WINOBJ_OBJ | WINOUT_WINOBJ_CLR | WINOUT_WIN01_BG0 | WINOUT_WIN01_BG2 | WINOUT_WIN01_BG3 | WINOUT_WIN01_OBJ | WINOUT_WIN01_CLR);
        SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_OBJWIN_ON);
        SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT2_ALL | BLDCNT_EFFECT_BLEND | BLDCNT_TGT1_BG1);
        SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(8, 12));
        SetAnimBgAttribute(1, BG_ANIM_PRIORITY, 0);
        SetAnimBgAttribute(1, BG_ANIM_SCREEN_SIZE, 0);
        if (!IsContest())
            SetAnimBgAttribute(1, BG_ANIM_CHAR_BASE_BLOCK, 1);
    
        if (IsDoubleBattle() && !IsContest())
        {
            if (GetBattlerPosition(gBattleAnimAttacker) == B_POSITION_OPPONENT_RIGHT || GetBattlerPosition(gBattleAnimAttacker) == B_POSITION_PLAYER_LEFT)
            {
                if (IsBattlerSpriteVisible(BATTLE_PARTNER(gBattleAnimAttacker)) == TRUE)
                {
                    gSprites[gBattlerSpriteIds[BATTLE_PARTNER(gBattleAnimAttacker)]].oam.priority--;
                    SetAnimBgAttribute(1, BG_ANIM_PRIORITY, 1);
                    priorityChanged = TRUE;
                }
            }
        }
    
        if (IsContest())
        {
            species = gContestResources->moveAnim->species;
        }
        else
        {
            if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER)
                species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattleAnimAttacker]], MON_DATA_SPECIES);
            else
                species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattleAnimAttacker]], MON_DATA_SPECIES);
        }
    
        spriteId = GetAnimBattlerSpriteId(ANIM_ATTACKER);
        newSpriteId = CreateInvisibleSpriteCopy(gBattleAnimAttacker, spriteId, species);
    
        GetBattleAnimBg1Data(&animBg);
        AnimLoadCompressedBgTilemap(animBg.bgId, gMetalShineTilemap);
        AnimLoadCompressedBgGfx(animBg.bgId, gMetalShineGfx, animBg.tilesOffset);
        LoadCompressedPalette(gMetalShinePalette, BG_PLTT_ID(animBg.paletteId), PLTT_SIZE_4BPP);
    
        gBattle_BG1_X = -gSprites[spriteId].x + 96;
        gBattle_BG1_Y = -gSprites[spriteId].y + 32;
        paletteNum = 16 + gSprites[spriteId].oam.paletteNum;
    
        if (gBattleAnimArgs[1] == 0)
            SetGrayscaleOrOriginalPalette(paletteNum, FALSE);
        else
            BlendPalette(BG_PLTT_ID(paletteNum), 16, 11, gBattleAnimArgs[2]);
    
        gTasks[taskId].data[0] = newSpriteId;
        gTasks[taskId].data[1] = gBattleAnimArgs[0];
        gTasks[taskId].data[2] = gBattleAnimArgs[1];
        gTasks[taskId].data[3] = gBattleAnimArgs[2];
        gTasks[taskId].data[6] = priorityChanged;
        gTasks[taskId].func = AnimTask_MetallicShine_StepCopy;
    }
    
    static void AnimTask_MetallicShine_StepCopy(u8 taskId)
    {
        struct BattleAnimBgData animBg;
        u16 paletteNum;
        u8 spriteId;
    
        gTasks[taskId].data[10] += 4;
        gBattle_BG1_X -= 4;
        if (gTasks[taskId].data[10] == 128)
        {
            gTasks[taskId].data[10] = 0;
            gBattle_BG1_X += 128;
            gTasks[taskId].data[11]++;
            if (gTasks[taskId].data[11] == 2)
            {
                spriteId = GetAnimBattlerSpriteId(ANIM_ATTACKER);
                paletteNum = 16 + gSprites[spriteId].oam.paletteNum;
                if (gTasks[taskId].data[1] == 0)
                    SetGrayscaleOrOriginalPalette(paletteNum, TRUE);
    
                DestroySprite(&gSprites[gTasks[taskId].data[0]]);
                GetBattleAnimBg1Data(&animBg);
                ClearBattleAnimBg(animBg.bgId);
                if (gTasks[taskId].data[6] == 1)
                    gSprites[gBattlerSpriteIds[BATTLE_PARTNER(gBattleAnimAttacker)]].oam.priority++;
            }
            else if (gTasks[taskId].data[11] == 3)
            {
                gBattle_WIN0H = 0;
                gBattle_WIN0V = 0;
                SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_BG_ALL | WININ_WIN0_OBJ | WININ_WIN0_CLR | WININ_WIN1_BG_ALL | WININ_WIN1_OBJ | WININ_WIN1_CLR);
                SetGpuReg(REG_OFFSET_WINOUT, WINOUT_WINOBJ_BG_ALL | WINOUT_WINOBJ_OBJ | WINOUT_WINOBJ_CLR | WINOUT_WIN01_BG_ALL | WINOUT_WIN01_OBJ | WINOUT_WIN01_CLR);
                if (!IsContest())
                    SetAnimBgAttribute(1, BG_ANIM_CHAR_BASE_BLOCK, 0);
    
                SetGpuReg(REG_OFFSET_DISPCNT, GetGpuReg(REG_OFFSET_DISPCNT) ^ DISPCNT_OBJWIN_ON);
                SetGpuReg(REG_OFFSET_BLDCNT, 0);
                SetGpuReg(REG_OFFSET_BLDALPHA, 0);
                DestroyAnimVisualTask(taskId);
            }
        }
    }
    
    #undef tTimer
     
    Last edited:
    453
    Posts
    6
    Years
    • Seen yesterday
    Hi all!

    I am trying to add a feature where an animation is shown when a pokemon enters battle, similarly to the shiny animation. For demo purposes, the condition for this animation triggering is that the mon's species is Treecko. However, I've encountered a problem as the game is softlocked after showing the animation, so my guess is that some sort of return statement or callbacks should be involved, but I'm not skilled enough in C to debug this myself. Any help in making the battle resume normally after the animation would be immensely appreciated!
    ...

    The regular shiny task eventually runs the line:
    Code:
    gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = TRUE;
    While your custom task doesn't. That's probably the reason the animation never ends.
     
    23
    Posts
    6
    Years
    • Seen Nov 28, 2023
    The regular shiny task eventually runs the line:
    Code:
    gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = TRUE;
    While your custom task doesn't. That's probably the reason the animation never ends.

    Oh wow, I can't believe I missed that. Thank you so much, that did the trick!
     
    Back
    Top