Registrarse

[Dis - Gráficos] Animación Pokemon estilo blanco y negro

Estado
Cerrado para nuevas respuestas.

Thorec_A_C

Héroe de WaH
Por fin me he decidido a dar el salto del romhacking a decomp, nunca he tenido un proyecto definido como tal pero me gusta "cacharrear" por así decirlo.

La cuestión es que estoy intentando simular las animaciones de los pokemon en combate al estilo blanco y negro. De forma mucho más básica eso sí. He aplicado un loop a la animación, el problema es que el combate no se inicia nunca. En el menú pokemon la animación funciona correctamente pero en el combate no. Entiendo que es porque las batallas están programadas para esperar a que finalice la animación del pokemon para iniciar el combate.

¿Alguién sabe qué tengo que modificar para que el combate no "espere" por la animación de pokemon para iniciarse? A ver si algún veterano en decomp me puede ayudar u orientarme.

Dejo un gif para que se entienda mejor.

pkanim.gif
 

Kaktus

Miembro insignia
Miembro insignia
Por fin me he decidido a dar el salto del romhacking a decomp, nunca he tenido un proyecto definido como tal pero me gusta "cacharrear" por así decirlo.

La cuestión es que estoy intentando simular las animaciones de los pokemon en combate al estilo blanco y negro. De forma mucho más básica eso sí. He aplicado un loop a la animación, el problema es que el combate no se inicia nunca. En el menú pokemon la animación funciona correctamente pero en el combate no. Entiendo que es porque las batallas están programadas para esperar a que finalice la animación del pokemon para iniciar el combate.

¿Alguién sabe qué tengo que modificar para que el combate no "espere" por la animación de pokemon para iniciarse? A ver si algún veterano en decomp me puede ayudar u orientarme.

Dejo un gif para que se entienda mejor.

Ver el archivo adjunto 4572
Pues a simple vista no es algo fácil de deducir, porque no sabemos qué código has tocado, cómo y qué has añadido. Si pudieras dejar por aquí en una caja de código el o los fragmentos que has cambiado, indicándolos con comentarios, estaría genial para poder ayudarte. Si son varios cambios, te recomiendo que los pongas en spoilers separados según el archivo que has modificado, así lo tenemos todo organizado y sabemos donde buscar nosotros en el código original para ver los cambios que has hecho.

Estoy a la espera del código para seguir ayudándote ;)
 

Thorec_A_C

Héroe de WaH
Gracias por la respuesta y las indicaciones. Pues he modificado muy poca cosa del código, como comenté lo que hice fue añadir un loop en la animación de Poochyena para que se repitiera de forma indefinida.

En /src/data/pokemon_graphics/front_pic_anims.h

Modifique esto:
Código:
static const union AnimCmd sAnim_POOCHYENA_1[] =
{
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_FRAME(1, 44),
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_END,
};
Por:
Código:
static const union AnimCmd sAnim_POOCHYENA_1[] =
{
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_FRAME(1, 44),
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_LOOP(1),
    ANIMCMD_JUMP(0),
};

Cogí la idea de Lunos en el post. Como comenté la cosa es que el combate no se inicia nunca, "espera" a que finalice la animación pero como está en loop nunca finaliza.

Entiendo que la solución sería modificar el código que controla el inicio del combate para que no espere a que finalice la animación del Pokemon o no tenga en cuenta la animación. He mirado por alto en data/battle_scripts_1 y src/battle_message.c, ya que en otras modificaciones del combate he visto que se modificaban esos archivos pero no tengo mucha idea la verdad jeje. Las cosas que voy sacando son a base de deducción y ensayo y error. Soy novato en decomp y no tengo conocimientos de programación.
 

Kaktus

Miembro insignia
Miembro insignia
Gracias por la respuesta y las indicaciones. Pues he modificado muy poca cosa del código, como comenté lo que hice fue añadir un loop en la animación de Poochyena para que se repitiera de forma indefinida.

En /src/data/pokemon_graphics/front_pic_anims.h

Modifique esto:
Código:
static const union AnimCmd sAnim_POOCHYENA_1[] =
{
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_FRAME(1, 44),
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_END,
};
Por:
Código:
static const union AnimCmd sAnim_POOCHYENA_1[] =
{
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_FRAME(1, 44),
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_LOOP(1),
    ANIMCMD_JUMP(0),
};

Cogí la idea de Lunos en el post. Como comenté la cosa es que el combate no se inicia nunca, "espera" a que finalice la animación pero como está en loop nunca finaliza.

Entiendo que la solución sería modificar el código que controla el inicio del combate para que no espere a que finalice la animación del Pokemon o no tenga en cuenta la animación. He mirado por alto en data/battle_scripts_1 y src/battle_message.c, ya que en otras modificaciones del combate he visto que se modificaban esos archivos pero no tengo mucha idea la verdad jeje. Las cosas que voy sacando son a base de deducción y ensayo y error. Soy novato en decomp y no tengo conocimientos de programación.
No estás finalizando el AnimCmd, puede ser una de las causas, aunque entiendo que es porque usas el jump. Ahora mismo no tengo muy claro como funcionan estos dos AnimCmd, así que por intuición, te diría que dejaras sólo el bucle, quitaras el jump, y terminaras las animaciones con AnimCmd_End, para ver si esto cumple con lo que quieres.

Si no, en otro momento me pongo a echarle un ojo. Anyway, es cuestión de probar parámetros en estas opciones y entender que hacen (si no están ya documentadas, claro, pero no es el caso).

Cambia tu código por esto

Código:
static const union AnimCmd sAnim_POOCHYENA_1[] =
{
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_FRAME(1, 44),
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_LOOP(1),
    ANIMCMD_END,
};
 

Thorec_A_C

Héroe de WaH
He aplicado el código que me indicas:
Código:
static const union AnimCmd sAnim_POOCHYENA_1[] =
{
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_FRAME(1, 44),
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_LOOP(1),
    ANIMCMD_END,
};
Pero entonces el loop no funciona, es decir se inicia el combate, se ejecuta la animación del Pokemon una vez, como de forma normal, y te permite seleccionar la acción. Pero la animación no se repite en bucle.

He intentado el código:
Código:
static const union AnimCmd sAnim_POOCHYENA_1[] =
{
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_FRAME(1, 44),
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_LOOP(1),
    ANIMCMD_JUMP(0),
    ANIMCMD_END,
};
Y el resultado es el mismo que en el de mi post inicial, la animación se repite en bucle pero el combate no avanza a partir del texto "Go! Torchic!".

Sigo pensando que debe haber otro código que controla el comportamiento de las transiciones del combate que evita que continué el mismo hasta que se termine de ejecutar la animación del Pokemon. Lo digo porque en el menú Pokemon este último código funciona perfectamente, la animación se ejecuta indefinidamente pero permite el uso de todas las opciones del menú.

Dejo el ejemplo en un gif:

 

Kaktus

Miembro insignia
Miembro insignia
He estado 5 minutos buscando y he encontrado estas dos funciones en gflib/sprite.c

C:
void StartSpriteAffineAnim(struct Sprite *sprite, u8 animNum)
{
    u8 matrixNum = GetSpriteMatrixNum(sprite);
    AffineAnimStateStartAnim(matrixNum, animNum);
    sprite->affineAnimBeginning = TRUE;
    sprite->affineAnimEnded = FALSE;
}
y

C:
void ChangeSpriteAffineAnim(struct Sprite *sprite, u8 animNum)
{
    u8 matrixNum = GetSpriteMatrixNum(sprite);
    sAffineAnimStates[matrixNum].animNum = animNum;
    sprite->affineAnimBeginning = TRUE;
    sprite->affineAnimEnded = FALSE;
}
Si te fijas, al final de ambas funciones dice sprite->affineAnimEnded = FALSE; Prueba a cambiar el de ambas funciones a TRUE, y dejar la animación del Pokémon en cuestión como al principio

C:
static const union AnimCmd sAnim_POOCHYENA_1[] =
{
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_FRAME(1, 44),
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_LOOP(1),
    ANIMCMD_JUMP(0),
};

Si con ese parámetro consigues el efecto deseado, ya sabes por donde ir tirando, si no, a seguir analizando código hasta encontrar lo que nos interesa. (Porque está claro que si en la summaryscreen se puede hacer el loop sin problemas, en batalla también se debe poder.
 

Thorec_A_C

Héroe de WaH
Lo primero muchas gracias por el interés y la ayuda. Te comento, he realizado los cambios que me indicas y por desgracia el resultado es el mismo, no ha cambiado nada. La animación se repite indefinidamente pero el combate no avanza.

También puedo confirmar que el combate no avanza por algún código que le indica que espere a finalizar la animación, porque además de lo comentado sobre el menú pokemon, he probado con este código:
Código:
static const union AnimCmd sAnim_POOCHYENA_1[] =
{
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_FRAME(1, 44),
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_LOOP(100),
};
Quitando el JUMP y asignado un valor al loop y la animación se repite durante 30-40 segundos, mientras el combate no avanza, pero tras ese tiempo termina el loop, finaliza la animación y salta de inmediato del "Go! Torchic!" al menú de combate.

He estado mirando en src/battle_anim.c y aparece la función: "void LaunchBattleAnimation(const u8 *const animsTable[], u16 tableId, bool8 isMoveAnim)", y dentro de la misma aparece este trozo de código:
Código:
    for (i = 0; i < ANIM_ARGS_COUNT; i++)
        gBattleAnimArgs[i] = 0;

    sMonAnimTaskIdArray[0] = 0xFF;
    sMonAnimTaskIdArray[1] = 0xFF;
    sBattleAnimScriptPtr = animsTable[tableId];
    gAnimScriptActive = TRUE;
    gAnimFramesToWait = 0;
    gAnimScriptCallback = RunAnimScriptCommand;
¿Podrían estar las modificaciones necesarias en ese código?
 

Kaktus

Miembro insignia
Miembro insignia
Lo primero muchas gracias por el interés y la ayuda. Te comento, he realizado los cambios que me indicas y por desgracia el resultado es el mismo, no ha cambiado nada. La animación se repite indefinidamente pero el combate no avanza.

También puedo confirmar que el combate no avanza por algún código que le indica que espere a finalizar la animación, porque además de lo comentado sobre el menú pokemon, he probado con este código:
Código:
static const union AnimCmd sAnim_POOCHYENA_1[] =
{
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_FRAME(1, 44),
    ANIMCMD_FRAME(0, 10),
    ANIMCMD_LOOP(100),
};
Quitando el JUMP y asignado un valor al loop y la animación se repite durante 30-40 segundos, mientras el combate no avanza, pero tras ese tiempo termina el loop, finaliza la animación y salta de inmediato del "Go! Torchic!" al menú de combate.

He estado mirando en src/battle_anim.c y aparece la función: "void LaunchBattleAnimation(const u8 *const animsTable[], u16 tableId, bool8 isMoveAnim)", y dentro de la misma aparece este trozo de código:
Código:
    for (i = 0; i < ANIM_ARGS_COUNT; i++)
        gBattleAnimArgs[i] = 0;

    sMonAnimTaskIdArray[0] = 0xFF;
    sMonAnimTaskIdArray[1] = 0xFF;
    sBattleAnimScriptPtr = animsTable[tableId];
    gAnimScriptActive = TRUE;
    gAnimFramesToWait = 0;
    gAnimScriptCallback = RunAnimScriptCommand;
¿Podrían estar las modificaciones necesarias en ese código?
¡Correcto!

Vas muy bien encaminado. Sobretodo, céntrate en gAnimScriptActive = TRUE;. Quizás en false, no se congele. Por mi parte he estado mirando por encima y en ese mismo archivo, encontrarás la función ScriptCmd_end, que contiene este fragmento, quizás pueda serte de utilidad.

C:
if (!continuousAnim) // May have been used for debug?
    {
        m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 256);
        if (!IsContest())
        {
            sub_80A8278();
            UpdateOamPriorityInAllHealthboxes(1);
        }
        gAnimScriptActive = FALSE;
    }
¡Vas muy bien, tú no tengas miedo a cambiar cosas para probar! ;)
 

Thorec_A_C

Héroe de WaH
Comento los cambios que he probado:

Código:
    for (i = 0; i < ANIM_ARGS_COUNT; i++)
        gBattleAnimArgs[i] = 0;

    sMonAnimTaskIdArray[0] = 0xFF;
    sMonAnimTaskIdArray[1] = 0xFF;
    sBattleAnimScriptPtr = animsTable[tableId];
    gAnimScriptActive = FALSE;
    gAnimFramesToWait = 0;
    gAnimScriptCallback = RunAnimScriptCommand;
Cambié el valor de "gAnimScriptActive" a FALSE. También probé a cambiar el valor de "gAnimFramesToWait =" a uno distinto de "0" pero ningún cambio.

Luego probe a cambiar los valores de "continuousAnim" y "gAnimScriptActive" de las siguientes dos formas:
Código:
static void ScriptCmd_end(void)
{
    s32 i;
    bool32 continuousAnim = FALSE;   

...

    if (!continuousAnim) // May have been used for debug?
    {
        m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 256);
        if (!IsContest())
        {
            sub_80A8278();
            UpdateOamPriorityInAllHealthboxes(1);
        }
        gAnimScriptActive = TRUE;
    }
}
Código:
static void ScriptCmd_end(void)
{
    s32 i;
    bool32 continuousAnim = TRUE;   

...

    if (!continuousAnim) // May have been used for debug?
    {
        m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 256);
        if (!IsContest())
        {
            sub_80A8278();
            UpdateOamPriorityInAllHealthboxes(1);
        }
        gAnimScriptActive = TRUE;
    }
}
Pero tampoco se produjo cambio en el combate.

También localice "sMonAnimTaskIdArray" en el código de diversas funciones, por lo que probé a borrar esos códigos para ver si afectaba al combate de algún modo. También borré los códigos citados anteriormente. El resultado es que la animación del pokemon en combate no se ve afectada de ningún modo. Sin embargo al probar un combate con un pokemon sin animación en loop una vez que se selecciona el movimiento el juego se cierra.

Por ello empiezo a pensar que todas las funciones de src/battle_anim.c afectan a las de los movimientos de combate del Pokemon pero no a la animación del Pokemon al iniciarse el combate. La verdad que estoy bastante perdido 😓 No se por donde puedo seguir buscando.
 

kakarotto

Leyenda de WaH
Te sugiero que leas este post:

Para que puedas depurar el código porque si no es muy difícil dar en el clavo.

En efecto en la funcion LaunchBattleAnimation es dónde se ejecuta la amimación del pokémon, pero sólo una vez. La función para que se inicialice es la de StartSpriteAnim, por lo que jugando con ese parámetro puedas conseguir algo. También puedes mirar lo que que se mueva la battlebox arriba-abajo en bucle y sacar conclusiones.
 

Thorec_A_C

Héroe de WaH
Gracias, entonces leere el post que me recomiendas y cuando tenga un rato me pondré a investigar, a ver si consigo sacar en claro algo. Si hiciese algún avance lo dejaría por aquí.
 

Thorec_A_C

Héroe de WaH
Aun no he podido depurar el código porque no he instalado lo necesario pero tenía una investigación a medias que quería terminar. Os dejo lo que he sacado, os aviso es un tocho xD Por lo que lo dejo en spoiler.

Existen dos funciones que controlan la animación frontal del Pokemon: "PokemonSummaryDoMonAnimation" y "DoMonFrontSpriteAnimation". "PokemonSummaryDoMonAnimation" controla la animación en el menú principal mientras que "DoMonFrontSpriteAnimation" controla la animación en el resto de eventos del juego donde se muestra el frontal del Pokemon como puede ser el combate, la escena de evolución del Pokemon, de apertura de un huevo, el hall de la fama, etc.

La función "PokemonSummaryDoMonAnimation" está presente en el archivo: pokemon_summary_screen.c

La función "DoMonFrontSpriteAnimation" está presente en los archivos: contest_util.c, egg_hatch.c, evolution_scene.c, hall_of_fame.c, etc.

Amabas funciones están presentes en el archivo pokemon.c donde se encuentra el código principal, que es diferente para cada una.

Es debido a esta diferencia que el loop funciona perfectamente en el menú Pokemon pero no en los combates ni en el resto de escenas, ya que en estos casos el juego se queda esperando a que termine la animación para avanzar. Por tanto se deduce que para la función "DoMonFrontSpriteAnimation" hay algo en el código que le indica que espere mientras que en "PokemonSummaryDoMonAnimation" no.

En el caso de la escena de evolución existe una función específica llamada "EvoScene_DoMonAnimation" que controla la función "DoMonFrontSpriteAnimation" y otra dependiente de esta llamada "EvoScene_IsMonAnimFinished" que le dice al juego si ha finalizado la animación.

Si observamos los códigos de estas dos funciones vemos lo siguiente:
Código:
static void EvoScene_DoMonAnimation(u8 monSpriteId, u16 speciesId)
{
    DoMonFrontSpriteAnimation(&gSprites[monSpriteId], speciesId, FALSE, 0);
}

static bool32 EvoScene_IsMonAnimFinished(u8 monSpriteId)
{
    if (gSprites[monSpriteId].callback == SpriteCallbackDummy)
        return TRUE;

    return FALSE;
}
En "EvoScene_IsMonAnimFinished" indica que si el Pokemon deja de moverse (== SpriteCallbackDummy/TRUE) la animación ha finalizado y si aun no ha dejado de moverse (== SpriteCallbackDummy/FALSE) la animación no ha finalizado y la escena de evolución no puede continuar. Dando como resultado lo siguiente:


Por ello he modificado el código de la siguiente manera:
Código:
static bool32 EvoScene_IsMonAnimFinished(u8 monSpriteId)
{
    if (gSprites[monSpriteId].callback == SpriteCallbackDummy)
        return TRUE;

    return TRUE;
}
De esta forma se "engaña" al juego para que continue ya que en ambos casos el código indica que la animación ha finalizado, aunque en la realidad no es así ya que tiene aplicado el loop. Y el resultado es este:


Conseguido! Funciona perfectamente.

Ahora viene el problema, se podría suponer que existe una función así para el combate y para las otras escenas pero no es así, o al menos no están tan claras o no las encuentro. Volviendo al tema principal, la animación en combate estoy casi seguro de que la clave son estas tres funciones:

"SpriteCb_WildMonAnimate" en battle_main.c:
Código:
static void SpriteCb_WildMonAnimate(struct Sprite *sprite)
{
    if (!gPaletteFade.active)
    {
        BattleAnimateFrontSprite(sprite, sprite->sSpeciesId, FALSE, 1);
    }
}
"BattleAnimateFrontSprite" en pokemon.c:
Código:
void BattleAnimateFrontSprite(struct Sprite* sprite, u16 species, bool8 noCry, u8 arg3)
{
    if (gHitMarker & HITMARKER_NO_ANIMATIONS && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)))
        DoMonFrontSpriteAnimation(sprite, species, noCry, arg3 | 0x80);
    else
        DoMonFrontSpriteAnimation(sprite, species, noCry, arg3);
}
Y "DoMonFrontSpriteAnimation" en pokemon.c:
Código:
void DoMonFrontSpriteAnimation(struct Sprite* sprite, u16 species, bool8 noCry, u8 arg3)
{
    s8 pan;
    switch (arg3 & 0x7F)
    {
    case 0:
        pan = -25;
        break;
    case 1:
        pan = 25;
        break;
    default:
        pan = 0;
        break;
    }
    if (arg3 & 0x80)
    {
        if (!noCry)
            PlayCry1(species, pan);
        sprite->callback = SpriteCallbackDummy;
    }
    else
    {
        if (!noCry)
        {
            PlayCry1(species, pan);
            if (HasTwoFramesAnimation(species))
                StartSpriteAnim(sprite, 1);
        }
        if (sMonAnimationDelayTable[species - 1] != 0)
        {
            u8 taskId = CreateTask(Task_AnimateAfterDelay, 0);
            STORE_PTR_IN_TASK(sprite, taskId, 0);
            gTasks[taskId].data[2] = sMonFrontAnimIdsTable[species - 1];
            gTasks[taskId].data[3] = sMonAnimationDelayTable[species - 1];
        }
        else
        {
            LaunchAnimationTaskForFrontSprite(sprite, sMonFrontAnimIdsTable[species - 1]);
        }
        sprite->callback = SpriteCallbackDummy_2;
    }
}
Como podéis ver una controla a la otra de tal manera:
"SpriteCb_WildMonAnimate"->"BattleAnimateFrontSprite"->"DoMonFrontSpriteAnimation". He probado a modificar alguna cosa como el "FALSE, 1" en "SpriteCb_WildMonAnimate" pero no consigo nada. Creo que estoy cerca de dar con la tecla pero al depender de varias funciones como sospecho no soy capaz de deducir que cambiar. Dejo este tocho aquí por si a alguien con más conocimiento se le ocurre algo.
 

KevinXDE

Usuario mítico
Si nadie se anima a investigarlo antes me lo apunto a la lista, lo que pasa es que yo también tengo una investigación que quiero terminar antes 😂. La verdad es que has hecho un currazo con lo de la pantalla de evolución, seguro que en batallas también se puede fácilmente, es cuestión de ir tocando las funciones y ver qué ocurre.

Mucho ánimo con tu investigación, y en cuanto a esta, estaré al tanto por si alguien encuentra algo, y si no pues ya avisaré si me pongo a ello en adelante
 

Thorec_A_C

Héroe de WaH
Muchas gracias por la respuesta y por los animos. Por mi parte voy a seguir toqueteando funciones y probando cosas a ver si resuelvo el tema de los combates, la cosa es que creo que con mis conocimientos actuales poco recorrido me queda 😅 De todas formas aun me queda probar la depuración. Por supuesto si alguien se anima a investigar este tema se lo agradezco un montón, toda ayuda es bienvenida.
 

Thorec_A_C

Héroe de WaH
Bueno pues tras mucho mirar y probar... Conseguido!! El combate ya continúa con el loop.



Explico lo que he modificado para conseguirlo:

Como comenté en post anteriores la animación del combate parte de la función "SpriteCb_WildMonAnimate" y continúa a través de diversas funciones que controlan diferentes aspectos de la misma. De tal forma tenemos la cadena de funciones: "SpriteCb_WildMonAnimate"->"BattleAnimateFrontSprite"->"DoMonFrontSpriteAnimation"->"LaunchAnimationTaskForFrontSprite"->"Task_HandleMonAnimation". Y es en esta función donde está la clave, ya que es donde "finaliza" el control de la animación.

En "Task_HandleMonAnimation" en pokemon_animation.c tenemos el siguiente código:
Código:
static void Task_HandleMonAnimation(u8 taskId)
{
    u32 i;
    struct Sprite *sprite = ANIM_SPRITE(taskId);

    if (gTasks[taskId].tState == 0)
    {
        gTasks[taskId].tSaved0 = sprite->data[0];
        gTasks[taskId].tSaved2 = sprite->data[2];
        sprite->data[1] = 1;
        sprite->data[0] = 0;

        for (i = 2; i < ARRAY_COUNT(sprite->data); i++)
            sprite->data[i] = 0;

        sprite->callback = sMonAnimFunctions[gTasks[taskId].tAnimId];
        sUnknown_03001274 = FALSE;

        gTasks[taskId].tState++;
    }
    if (sprite->callback == SpriteCallbackDummy)
    {
        sprite->data[0] = gTasks[taskId].tSaved0;
        sprite->data[2] = gTasks[taskId].tSaved2;
        sprite->data[1] = 0;

        DestroyTask(taskId);
    }
}
Si nos fijamos aparecen dos condiciones una donde el juego entiende que la animación no ha finalizado "if (gTasks[taskId].tState == 0)" y otra donde considera que la animación ha terminado y el combate puede continuar "if (sprite->callback == SpriteCallbackDummy)". ¿Os suena esta última? Es muy similar a lo que modifiqué con el tema de la evolución. Entonces modifico el código de esta forma:
Código:
static void Task_HandleMonAnimation(u8 taskId)
{
    u32 i;
    struct Sprite *sprite = ANIM_SPRITE(taskId);

    if (gTasks[taskId].tState == 0)
    {
        gTasks[taskId].tSaved0 = sprite->data[0];
        gTasks[taskId].tSaved2 = sprite->data[2];
        sprite->data[1] = 1;
        sprite->data[0] = 0;

        for (i = 2; i < ARRAY_COUNT(sprite->data); i++)
            sprite->data[i] = 0;

        sprite->callback = SpriteCallbackDummy;
        sUnknown_03001274 = FALSE;

        gTasks[taskId].tState++;
    }
    if (sprite->callback == SpriteCallbackDummy)
    {
        sprite->data[0] = gTasks[taskId].tSaved0;
        sprite->data[2] = gTasks[taskId].tSaved2;
        sprite->data[1] = 0;

        DestroyTask(taskId);
    }
}
Así se "engaña" al juego para que crea que la animación ha terminado, aunque no sea así, y permita continuar el combate. Lo bueno de esto es que afecta a todas las escenas de animación que contengan la función "DoMonFrontSpriteAnimation" por lo que no habría que modificar nada más para que funcionase en las escenas de evolución, apertura de un huevo, hall de la fama, etc. Esto aun tengo que mirarlo bien, sólo he probado que funciones con la evolución.

Ahora viene el "pero", sí, siempre hay un pero... 😒 En el combate pokemon si vamos al menú o a la mochila, al regresar la animación desaparece.



Creo que es porque:

El juego "limpia" la escena de combate al entrar en el menú y al volver la "reconstruye" pero como entiende que la animación sólo se ejecuta una vez al inicio del combate no la tiene en cuenta a la hora de "reconstruir". Investigando he visto que existe el archivo: reshow_battle_screen.c, donde aparece la función "CreateBattlerSprite(u8 battler)" con el código:
Código:
static void CreateBattlerSprite(u8 battler)
{
    if (battler < gBattlersCount)
    {
        u8 posY;

        if (gBattleSpritesDataPtr->battlerData[battler].behindSubstitute)
            posY = GetSubstituteSpriteDefault_Y(battler);
        else
            posY = GetBattlerSpriteDefault_Y(battler);

        if (GetBattlerSide(battler) != B_SIDE_PLAYER)
        {
            if (GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_HP) == 0)
                return;
            if (gBattleScripting.monCaught) // Don't create opponent sprite if it has been caught.
                return;

            SetMultiuseSpriteTemplateToPokemon(GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES), GetBattlerPosition(battler));
            gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, GetBattlerSpriteCoord(battler, 2), posY, GetBattlerSpriteSubpriority(battler));
            gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler;
            gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy;
            gSprites[gBattlerSpriteIds[battler]].data[0] = battler;
            gSprites[gBattlerSpriteIds[battler]].data[2] = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES);

            StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], gBattleMonForms[battler]);
            if (gBattleSpritesDataPtr->battlerData[battler].transformSpecies == SPECIES_CASTFORM)
                gSprites[gBattlerSpriteIds[battler]].anims = gMonFrontAnimsPtrTable[SPECIES_CASTFORM];
        }
        else if (gBattleTypeFlags & BATTLE_TYPE_SAFARI && battler == B_POSITION_PLAYER_LEFT)
        {
            SetMultiuseSpriteTemplateToTrainerBack(gSaveBlock2Ptr->playerGender, GetBattlerPosition(B_POSITION_PLAYER_LEFT));
            gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, 0x50,
                                                (8 - gTrainerBackPicCoords[gSaveBlock2Ptr->playerGender].size) * 4 + 80,
                                                 GetBattlerSpriteSubpriority(0));
            gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler;
            gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy;
            gSprites[gBattlerSpriteIds[battler]].data[0] = battler;
        }
        else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL && battler == B_POSITION_PLAYER_LEFT)
        {
            SetMultiuseSpriteTemplateToTrainerBack(TRAINER_BACK_PIC_WALLY, GetBattlerPosition(0));
            gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, 0x50,
                                                (8 - gTrainerBackPicCoords[TRAINER_BACK_PIC_WALLY].size) * 4 + 80,
                                                 GetBattlerSpriteSubpriority(0));
            gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler;
            gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy;
            gSprites[gBattlerSpriteIds[battler]].data[0] = battler;
        }
        else
        {
            if (GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_HP) == 0)
                return;

            SetMultiuseSpriteTemplateToPokemon(GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES), GetBattlerPosition(battler));
            gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, GetBattlerSpriteCoord(battler, 2), posY, GetBattlerSpriteSubpriority(battler));
            gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler;
            gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy;
            gSprites[gBattlerSpriteIds[battler]].data[0] = battler;
            gSprites[gBattlerSpriteIds[battler]].data[2] = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES);

            StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], gBattleMonForms[battler]);
            if (gBattleSpritesDataPtr->battlerData[battler].transformSpecies == SPECIES_CASTFORM)
                gSprites[gBattlerSpriteIds[battler]].anims = gMonFrontAnimsPtrTable[SPECIES_CASTFORM];
        }

        gSprites[gBattlerSpriteIds[battler]].invisible = gBattleSpritesDataPtr->battlerData[battler].invisible;
    }
}
Creo que aquí está la clave, concretamente en "gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy;" que aparece varias veces a lo largo de la función. Ya que si lo quito ocurre esto:



Parece que controla el comportamiento del sprite, el problema es que como hemos visto anteriormente el juego considera "SpriteCallbackDummy" como animación finalizada o Pokemon sin animación, por eso se genera el sprite estático.

Después de este nuevo tocho decir que vuelvo a estar perdido 😆 no se que modificar o por que modificarlo. Lo dejo por aquí por si alguien tiene alguna idea o se le ocurre probar algo.
 

kakarotto

Leyenda de WaH
Enhorabuena, yo lo puliría más para que empezará la animacion justo después de sacar al pokémon, pero vas por buen camino.

El dummycallback es un atributo del sprite que es como un template que no muestra animacion. Si se lo quitas y refresca pues se vuelve loco el juego jajaj.

Puedes crear tu propio callback para que siga continuando la animacion. Si quieres le podemos echar un ojo y te ayudo crack.
 

Thorec_A_C

Héroe de WaH
Muchas gracias, pues estaría genial poder crear un callback para que continúe la animación, si me echas una mano te lo agradezco un montón, si quieres por privado o por aquí mismo me dices.

Hablando de callback te comento, en battle_main.c en una función aparece "sprite->callback = SpriteCb_WildMonAnimate", Y la función "SpriteCb_WildMonAnimate" es la que inicia la animación original. He probado sustituir "SpriteCallbackDummy" por esta en "CreateBattlerSprite" pero me manda definirla al compilar, lo hago como esta en su función original pero me da un error algo así como "formato no válido", hablo de memoria. Ya ves que me falta técnica y conocimientos para esto pero quizás a ti te sirva para crear el callback.
 

kakarotto

Leyenda de WaH
Agregame a discord para hablar mas seguido y detenido. Una vez sepas hacerlo, puedes postearlo aqui ;)
NO SE PUEDEN PONER TAGS DE DISCORD
 
Última edición por un moderador:
Estado
Cerrado para nuevas respuestas.
Arriba