mirror of
https://github.com/pret/pokefirered.git
synced 2026-09-30 21:10:27 -05:00
added mon animations to trainer battles, wild encounters and sprite visualizer
This commit is contained in:
@@ -71,11 +71,13 @@ void SpriteCallbackDummy_2(struct Sprite *sprite);
|
||||
void SpriteCB_FaintOpponentMon(struct Sprite *sprite);
|
||||
void SpriteCB_ShowAsMoveTarget(struct Sprite *sprite);
|
||||
void SpriteCB_HideAsMoveTarget(struct Sprite *sprite);
|
||||
void SpriteCB_OpponentMonFromBall(struct Sprite *sprite);
|
||||
void SpriteCB_AllyMon(struct Sprite *sprite);
|
||||
void SetIdleSpriteCallback(struct Sprite *sprite);
|
||||
void SpriteCB_FaintSlideAnim(struct Sprite *sprite);
|
||||
void DoBounceEffect(u8 battler, u8 which, s8 delta, s8 amplitude);
|
||||
void EndBounceEffect(u8 battler, u8 which);
|
||||
void SpriteCB_PlayerMonFromBall(struct Sprite *sprite);
|
||||
void SpriteCB_PlayerThrowInit(struct Sprite *sprite);
|
||||
void UpdatePlayerPosInThrowAnim(struct Sprite *sprite);
|
||||
void BeginBattleIntroDummy(void);
|
||||
|
||||
@@ -36,14 +36,18 @@
|
||||
#define P_SHEDINJA_BALL GEN_LATEST // Since Gen 4, Shedinja requires a Poké Ball for its evolution. In Gen 3, Shedinja inherits Nincada's Ball.
|
||||
#define P_KADABRA_EVERSTONE GEN_LATEST // Since Gen 4, Kadabra can evolve even when holding an Everstone.
|
||||
#define P_SHUCKLE_BERRY_JUICE GEN_LATEST // In Gen 2, Shuckle had a 1/16 chance of converting Berry that it's holding into Berry Juice. Enabling this will allow Shuckle to do this with an Oran Berry, which is the spiritual succesor of the Berry item.
|
||||
#define P_ARCEUS_UNIQUE_FORM_ICONS GEN_LATEST // Since Gen 9, Arceus additionally changes its icon to reflect its current form.
|
||||
|
||||
// Other settings
|
||||
#define P_CUSTOM_GENDER_DIFF_ICONS TRUE // If TRUE, will give more Pokémon custom icons for their female forms, i.e. Hippopotas and Hippowdon
|
||||
#define P_FOOTPRINTS TRUE // If TRUE, Pokémon will have footprints (as was the case up to Gen 5 and in BDSP). Disabling this saves some ROM space.
|
||||
#define P_CRIES_ENABLED TRUE // If TRUE, Pokémon will have cries. Disabling this saves around a LOT of ROM space (over 25%!), but instead we recommend disabling individual unused Pokémon families in include/config/species_enabled.h.
|
||||
#define P_LEGENDARY_PERFECT_IVS GEN_LATEST // Since Gen 6, Legendaries, Mythicals and Ultra Beasts found in the wild or given through gifts have at least 3 perfect IVs.
|
||||
#define P_EV_CAP GEN_LATEST // Since Gen 6, the max EVs per stat is 252 instead of 255.
|
||||
#define P_SHOW_TERA_TYPE GEN_8 // Since Gen 9, the Tera Type is shown on the summary screen.
|
||||
#define P_TM_LITERACY GEN_LATEST // Since Gen 6, TM illiterate Pokémon can learn TMs that teach moves that are in their level-up learnsets.
|
||||
#define P_EGG_CYCLE_LENGTH GEN_LATEST // Since Gen 8, egg cycles take half as many steps as before.
|
||||
#define P_TWO_FRAME_FRONT_SPRITES TRUE // In Pokémon Emerald, Pokémon front sprites always consist of two frames. This config can revert it to only use the first frame, as is the case in the other Gen 3 games.
|
||||
|
||||
// Learnset helper toggles
|
||||
#define P_LEARNSET_HELPER_TEACHABLE TRUE // If TRUE, teachable_learnsets.h will be populated by tools/learnset_helpers/teachable.py using the included JSON files based on available TMs and tutors.
|
||||
|
||||
@@ -282,11 +282,12 @@ struct Pokemon
|
||||
|
||||
struct MonSpritesGfxManager
|
||||
{
|
||||
u8 numSprites:4;
|
||||
u8 battlePosition:4;
|
||||
u8 numFrames;
|
||||
u8 active;
|
||||
u8 mode;
|
||||
u32 numSprites:4;
|
||||
u32 numSprites2:4; // Never read
|
||||
u32 battlePosition:4;
|
||||
u32 numFrames:8;
|
||||
u32 active:8;
|
||||
u32 mode:4;
|
||||
u32 dataSize;
|
||||
u8 *spriteBuffer;
|
||||
u8 **spritePointers;
|
||||
@@ -294,6 +295,18 @@ struct MonSpritesGfxManager
|
||||
struct SpriteFrameImage *frameImages;
|
||||
};
|
||||
|
||||
enum {
|
||||
MON_SPR_GFX_MODE_NORMAL,
|
||||
MON_SPR_GFX_MODE_BATTLE,
|
||||
MON_SPR_GFX_MODE_FULL_PARTY,
|
||||
};
|
||||
|
||||
enum {
|
||||
MON_SPR_GFX_MANAGER_A,
|
||||
MON_SPR_GFX_MANAGER_B, // Nothing ever sets up this manager.
|
||||
MON_SPR_GFX_MANAGERS_COUNT
|
||||
};
|
||||
|
||||
struct BattleTowerPokemon
|
||||
{
|
||||
/*0x00*/ u16 species;
|
||||
@@ -671,7 +684,7 @@ extern const u32 gExperienceTables[][MAX_LEVEL + 1];
|
||||
// extern const u16 *const gLevelUpLearnsets[];
|
||||
extern const u8 gFacilityClassToPicIndex[];
|
||||
extern const u8 gFacilityClassToTrainerClass[];
|
||||
extern const struct SpriteTemplate gSpriteTemplates_Battlers[];
|
||||
extern const struct SpriteTemplate gBattlerSpriteTemplates[];
|
||||
extern const u8 gPPUpGetMask[];
|
||||
extern const u32 sExpCandyExperienceTable[];
|
||||
extern const struct Ability gAbilitiesInfo[];
|
||||
@@ -816,6 +829,9 @@ void SetMonPreventsSwitchingString(void);
|
||||
void SetWildMonHeldItem(void);
|
||||
bool8 IsMonShiny(struct Pokemon *mon);
|
||||
u8 *GetTrainerPartnerName(void);
|
||||
void BattleAnimateFrontSprite(struct Sprite *sprite, u16 species, bool8 noCry, u8 panMode);
|
||||
void DoMonFrontSpriteAnimation(struct Sprite *sprite, u16 species, bool8 noCry, u8 panModeAnimFlag);
|
||||
void BattleAnimateBackSprite(struct Sprite *sprite, u16 species);
|
||||
u8 GetPlayerPartyHighestLevel(void);
|
||||
u16 GetUnionRoomTrainerPic(void);
|
||||
u16 GetUnionRoomTrainerClass(void);
|
||||
@@ -823,10 +839,11 @@ void CreateEnemyEventMon(void);
|
||||
u16 FacilityClassToPicIndex(u16 facilityClass);
|
||||
u16 PlayerGenderToFrontTrainerPicId(u8 playerGender);
|
||||
void HandleSetPokedexFlag(u16 nationalNum, u8 caseId, u32 personality);
|
||||
bool32 HasTwoFramesAnimation(u16 species);
|
||||
bool8 CheckBattleTypeGhost(struct Pokemon *mon, u8 bank);
|
||||
struct MonSpritesGfxManager *CreateMonSpritesGfxManager(u8 battlePosition, u8 mode);
|
||||
void DestroyMonSpritesGfxManager(void);
|
||||
u8 *MonSpritesGfxManager_GetSpritePtr(u8 bufferId);
|
||||
void DestroyMonSpritesGfxManager(u8 managerId);
|
||||
u8 *MonSpritesGfxManager_GetSpritePtr(u8 managerId, u8 spriteNum);
|
||||
u16 GetFormSpeciesId(u16 speciesId, u8 formId);
|
||||
u8 GetFormIdFromFormSpeciesId(u16 formSpeciesId);
|
||||
u16 GetFormChangeTargetSpecies(struct Pokemon *mon, u16 method, u32 arg);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#ifndef GUARD_POKEMON_ANIMATION_H
|
||||
#define GUARD_POKEMON_ANIMATION_H
|
||||
|
||||
// u8 GetSpeciesBackAnimSet(u16 species);
|
||||
// void LaunchAnimationTaskForFrontSprite(struct Sprite *sprite, u8 frontAnimId);
|
||||
// void StartMonSummaryAnimation(struct Sprite *sprite, u8 frontAnimId);
|
||||
// void LaunchAnimationTaskForBackSprite(struct Sprite *sprite, u8 backAnimSet);
|
||||
// void SetSpriteCB_MonAnimDummy(struct Sprite *sprite);
|
||||
u8 GetSpeciesBackAnimSet(u16 species);
|
||||
void LaunchAnimationTaskForFrontSprite(struct Sprite *sprite, u8 frontAnimId);
|
||||
void StartMonSummaryAnimation(struct Sprite *sprite, u8 frontAnimId);
|
||||
void LaunchAnimationTaskForBackSprite(struct Sprite *sprite, u8 backAnimSet);
|
||||
void SetSpriteCB_MonAnimDummy(struct Sprite *sprite);
|
||||
|
||||
// Pokémon back animation sets
|
||||
#define BACK_ANIM_NONE 0
|
||||
|
||||
@@ -10,6 +10,7 @@ void SummaryScreen_DestroyUnknownTask(void);
|
||||
u8 GetLastViewedMonIndex(void);
|
||||
void ShowPokemonSummaryScreen(struct Pokemon * party, u8 cursorPos, u8 lastIdx, void (*callback)(void), u8 a4);
|
||||
void SetPokemonSummaryScreenMode(u8);
|
||||
void SummaryScreen_SetAnimDelayTaskId(u8 taskId);
|
||||
|
||||
// The Pokemon Summary Screen can operate in different modes. Certain features,
|
||||
// such as move re-ordering, are available in the different modes.
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#ifndef GUARD_TRAINER_POKEMON_SPRITES_H
|
||||
#define GUARD_TRAINER_POKEMON_SPRITES_H
|
||||
|
||||
#include "global.h"
|
||||
// For the flags argument of CreateMonPicSprite_Affine
|
||||
#define MON_PIC_AFFINE_BACK 0
|
||||
#define MON_PIC_AFFINE_FRONT 1
|
||||
#define MON_PIC_AFFINE_NONE 3
|
||||
#define F_MON_PIC_NO_AFFINE (1 << 7)
|
||||
|
||||
u16 CreateTrainerCardTrainerPicSprite(u16 species, bool8 isFrontPic, u16 destX, u16 destY, u8 paletteSlot, u8 windowId);
|
||||
u16 CreateMonPicSprite_Affine(u16 species, bool8 isShiny, u32 personality, u8 flags, s16 x, s16 y, u8 paletteSlot, u16 paletteTag);
|
||||
bool16 ResetAllPicSprites(void);
|
||||
u16 CreateTrainerPicSprite(u16 species, bool8 isFrontPic, s16 x, s16 y, u8 paletteSlot, u16 paletteTag);
|
||||
u16 FreeAndDestroyMonPicSprite(u16);
|
||||
|
||||
@@ -114,6 +114,10 @@ static void Cmd_visible(void);
|
||||
static void Cmd_teamattack_moveback(void);
|
||||
static void Cmd_teamattack_movefwd(void);
|
||||
static void Cmd_stopsound(void);
|
||||
static void Cmd_createvisualtaskontargets(void);
|
||||
static void Cmd_createspriteontargets(void);
|
||||
static void Cmd_createspriteontargets_onpos(void);
|
||||
static void Cmd_createdragondartsprite(void);
|
||||
|
||||
#include "data/battle_anim.h"
|
||||
|
||||
@@ -167,6 +171,10 @@ static void (*const sScriptCmdTable[])(void) =
|
||||
Cmd_teamattack_moveback, // 0x2D
|
||||
Cmd_teamattack_movefwd, // 0x2E
|
||||
Cmd_stopsound, // 0x2F
|
||||
Cmd_createvisualtaskontargets, // 0x30
|
||||
Cmd_createspriteontargets, // 0x31
|
||||
Cmd_createspriteontargets_onpos, // 0x32
|
||||
Cmd_createdragondartsprite, // 0x33
|
||||
};
|
||||
|
||||
void ClearBattleAnimationVars(void)
|
||||
@@ -412,6 +420,88 @@ static void Cmd_unloadspritegfx(void)
|
||||
ClearSpriteIndex(GET_TRUE_SPRITE_INDEX(index));
|
||||
}
|
||||
|
||||
static u8 GetBattleAnimMoveTargets(u8 battlerArgIndex, u8 *targets)
|
||||
{
|
||||
u8 numTargets = 0;
|
||||
u32 battlerAnimId = gBattleAnimArgs[battlerArgIndex]; // ANIM_xx input
|
||||
u32 i;
|
||||
u32 ignoredTgt = gBattlerAttacker;
|
||||
u32 target = GetBattlerMoveTargetType(gBattleAnimAttacker, gAnimMoveIndex);
|
||||
|
||||
switch (battlerAnimId)
|
||||
{
|
||||
case ANIM_ATTACKER:
|
||||
case ANIM_ATK_PARTNER:
|
||||
ignoredTgt = gBattlerTarget;
|
||||
break;
|
||||
case ANIM_TARGET:
|
||||
case ANIM_DEF_PARTNER:
|
||||
ignoredTgt = gBattlerAttacker;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (target)
|
||||
{
|
||||
case MOVE_TARGET_FOES_AND_ALLY:
|
||||
if (battlerAnimId == ANIM_ATTACKER)
|
||||
{
|
||||
targets[numTargets++] = gBattleAnimAttacker;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (i != gBattleAnimAttacker && IS_ALIVE_AND_PRESENT(i))
|
||||
targets[numTargets++] = i + MAX_BATTLERS_COUNT; // anim ids for battler ids
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOVE_TARGET_BOTH: // all opponents
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (i != ignoredTgt && !IsAlly(i, ignoredTgt) && IS_ALIVE_AND_PRESENT(i))
|
||||
targets[numTargets++] = i + MAX_BATTLERS_COUNT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
targets[0] = gBattleAnimArgs[battlerArgIndex]; // original
|
||||
numTargets = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return numTargets;
|
||||
}
|
||||
|
||||
static s16 GetSubpriorityForMoveAnim(u8 argVar)
|
||||
{
|
||||
s16 subpriority;
|
||||
|
||||
if (argVar & ANIMSPRITE_IS_TARGET)
|
||||
{
|
||||
argVar ^= ANIMSPRITE_IS_TARGET;
|
||||
if (argVar >= 64)
|
||||
argVar -= 64;
|
||||
else
|
||||
argVar *= -1;
|
||||
|
||||
subpriority = GetBattlerSpriteSubpriority(gBattleAnimTarget) + (s8)(argVar);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (argVar >= 64)
|
||||
argVar -= 64;
|
||||
else
|
||||
argVar *= -1;
|
||||
|
||||
subpriority = GetBattlerSpriteSubpriority(gBattleAnimAttacker) + (s8)(argVar);
|
||||
}
|
||||
|
||||
if (subpriority < 3)
|
||||
subpriority = 3;
|
||||
|
||||
return subpriority;
|
||||
}
|
||||
|
||||
// Create sprite from template and init data array with varargs
|
||||
// args: template, flags, va_args
|
||||
// flags:
|
||||
@@ -472,6 +562,89 @@ static void Cmd_createsprite(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void CreateSpriteOnTargets(const struct SpriteTemplate *template, u8 argVar, u8 battlerArgIndex, u8 argsCount, bool32 overwriteAnimTgt)
|
||||
{
|
||||
u32 i, battler;
|
||||
u8 targets[MAX_BATTLERS_COUNT];
|
||||
int ntargets;
|
||||
s16 subpriority;
|
||||
|
||||
for (i = 0; i < argsCount; i++)
|
||||
{
|
||||
gBattleAnimArgs[i] = T1_READ_16(sBattleAnimScriptPtr);
|
||||
sBattleAnimScriptPtr += 2;
|
||||
}
|
||||
|
||||
subpriority = GetSubpriorityForMoveAnim(argVar);
|
||||
|
||||
ntargets = GetBattleAnimMoveTargets(battlerArgIndex, targets);
|
||||
if (ntargets == 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ntargets; i++)
|
||||
{
|
||||
battler = GetAnimBattlerId(targets[i]);
|
||||
if (overwriteAnimTgt)
|
||||
gBattleAnimArgs[battlerArgIndex] = targets[i];
|
||||
|
||||
if (CreateSpriteAndAnimate(template,
|
||||
GetBattlerSpriteCoord(battler, BATTLER_COORD_X_2),
|
||||
GetBattlerSpriteCoord(battler, BATTLER_COORD_Y_PIC_OFFSET),
|
||||
subpriority) != MAX_SPRITES) // Don't increment the task count if the sprite couldn't be created(i.e. there are too many created sprites atm).
|
||||
{
|
||||
gAnimVisualTaskCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// will NOT overwrite gBattleAnimArgs
|
||||
static void Cmd_createspriteontargets_onpos(void)
|
||||
{
|
||||
const struct SpriteTemplate *template;
|
||||
u8 argVar;
|
||||
u8 argsCount;
|
||||
u8 battlerArgIndex;
|
||||
|
||||
sBattleAnimScriptPtr++;
|
||||
template = (const struct SpriteTemplate *)(T2_READ_32(sBattleAnimScriptPtr));
|
||||
sBattleAnimScriptPtr += 4;
|
||||
|
||||
argVar = sBattleAnimScriptPtr[0];
|
||||
sBattleAnimScriptPtr++;
|
||||
|
||||
battlerArgIndex = sBattleAnimScriptPtr[0];
|
||||
sBattleAnimScriptPtr++;
|
||||
|
||||
argsCount = sBattleAnimScriptPtr[0];
|
||||
sBattleAnimScriptPtr++;
|
||||
|
||||
CreateSpriteOnTargets(template, argVar, battlerArgIndex, argsCount, FALSE);
|
||||
}
|
||||
|
||||
// DOES overwrite gBattleAnimArgs
|
||||
static void Cmd_createspriteontargets(void)
|
||||
{
|
||||
const struct SpriteTemplate *template;
|
||||
u8 argVar;
|
||||
u8 argsCount;
|
||||
u8 battlerArgIndex;
|
||||
|
||||
sBattleAnimScriptPtr++;
|
||||
template = (const struct SpriteTemplate *)(T2_READ_32(sBattleAnimScriptPtr));
|
||||
sBattleAnimScriptPtr += 4;
|
||||
|
||||
argVar = sBattleAnimScriptPtr[0];
|
||||
sBattleAnimScriptPtr++;
|
||||
|
||||
battlerArgIndex = sBattleAnimScriptPtr[0];
|
||||
sBattleAnimScriptPtr++;
|
||||
|
||||
argsCount = sBattleAnimScriptPtr[0];
|
||||
sBattleAnimScriptPtr++;
|
||||
|
||||
CreateSpriteOnTargets(template, argVar, battlerArgIndex, argsCount, TRUE);
|
||||
}
|
||||
|
||||
static void Cmd_createvisualtask(void)
|
||||
{
|
||||
TaskFunc taskFunc;
|
||||
@@ -502,6 +675,51 @@ static void Cmd_createvisualtask(void)
|
||||
gAnimVisualTaskCount++;
|
||||
}
|
||||
|
||||
static void Cmd_createvisualtaskontargets(void)
|
||||
{
|
||||
TaskFunc taskFunc;
|
||||
u8 taskPriority;
|
||||
u8 taskId;
|
||||
u8 numArgs;
|
||||
u8 battlerArgIndex; // index in gBattleAnimArgs that has the battlerId
|
||||
s32 i;
|
||||
u8 targets[MAX_BATTLERS_COUNT] = {0};
|
||||
|
||||
sBattleAnimScriptPtr++;
|
||||
|
||||
taskFunc = (TaskFunc)T2_READ_32(sBattleAnimScriptPtr);
|
||||
sBattleAnimScriptPtr += 4;
|
||||
|
||||
taskPriority = sBattleAnimScriptPtr[0];
|
||||
sBattleAnimScriptPtr++;
|
||||
|
||||
battlerArgIndex = sBattleAnimScriptPtr[0];
|
||||
sBattleAnimScriptPtr++;
|
||||
|
||||
numArgs = sBattleAnimScriptPtr[0];
|
||||
sBattleAnimScriptPtr++;
|
||||
|
||||
// copy task arguments
|
||||
for (i = 0; i < numArgs; i++)
|
||||
{
|
||||
gBattleAnimArgs[i] = T1_READ_16(sBattleAnimScriptPtr);
|
||||
sBattleAnimScriptPtr += 2;
|
||||
}
|
||||
|
||||
numArgs = GetBattleAnimMoveTargets(battlerArgIndex, targets);
|
||||
if (numArgs == 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < numArgs; i++)
|
||||
{
|
||||
gBattleAnimArgs[battlerArgIndex] = targets[i];
|
||||
taskId = CreateTask(taskFunc, taskPriority);
|
||||
taskFunc(taskId);
|
||||
gAnimVisualTaskCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void Cmd_delay(void)
|
||||
{
|
||||
sBattleAnimScriptPtr++;
|
||||
@@ -687,6 +905,24 @@ static void Cmd_monbg(void)
|
||||
sBattleAnimScriptPtr++;
|
||||
}
|
||||
|
||||
u8 GetAnimBattlerId(u8 wantedBattler)
|
||||
{
|
||||
switch (wantedBattler)
|
||||
{
|
||||
case ANIM_ATTACKER:
|
||||
default:
|
||||
return gBattleAnimAttacker;
|
||||
case ANIM_TARGET:
|
||||
return gBattleAnimTarget;
|
||||
case ANIM_ATK_PARTNER:
|
||||
return BATTLE_PARTNER(gBattleAnimAttacker);
|
||||
case ANIM_DEF_PARTNER:
|
||||
return BATTLE_PARTNER(gBattleAnimTarget);
|
||||
case ANIM_PLAYER_LEFT ... ANIM_OPPONENT_RIGHT:
|
||||
return wantedBattler - MAX_BATTLERS_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
bool8 IsBattlerSpriteVisible(u8 battlerId)
|
||||
{
|
||||
u8 battler = battlerId;
|
||||
@@ -1785,22 +2021,63 @@ static void Cmd_stopsound(void)
|
||||
sBattleAnimScriptPtr++;
|
||||
}
|
||||
|
||||
u8 GetAnimBattlerId(u8 wantedBattler)
|
||||
static void Cmd_createdragondartsprite(void)
|
||||
{
|
||||
switch (wantedBattler)
|
||||
s32 i;
|
||||
struct SpriteTemplate template;
|
||||
u8 argVar;
|
||||
u8 argsCount;
|
||||
s16 subpriority;
|
||||
struct Pokemon *party = GetBattlerParty(gBattleAnimAttacker);
|
||||
|
||||
sBattleAnimScriptPtr++;
|
||||
|
||||
argVar = sBattleAnimScriptPtr[0];
|
||||
sBattleAnimScriptPtr++;
|
||||
argsCount = sBattleAnimScriptPtr[0];
|
||||
sBattleAnimScriptPtr++;
|
||||
|
||||
for (i = 0; i < argsCount; i++)
|
||||
{
|
||||
case ANIM_ATTACKER:
|
||||
default:
|
||||
return gBattleAnimAttacker;
|
||||
case ANIM_TARGET:
|
||||
return gBattleAnimTarget;
|
||||
case ANIM_ATK_PARTNER:
|
||||
return BATTLE_PARTNER(gBattleAnimAttacker);
|
||||
case ANIM_DEF_PARTNER:
|
||||
return BATTLE_PARTNER(gBattleAnimTarget);
|
||||
case ANIM_PLAYER_LEFT ... ANIM_OPPONENT_RIGHT:
|
||||
return wantedBattler - MAX_BATTLERS_COUNT;
|
||||
gBattleAnimArgs[i] = T1_READ_16(sBattleAnimScriptPtr);
|
||||
sBattleAnimScriptPtr += 2;
|
||||
}
|
||||
|
||||
subpriority = GetSubpriorityForMoveAnim(argVar);
|
||||
|
||||
if (GetMonData(&party[gBattlerPartyIndexes[gBattleAnimAttacker]], MON_DATA_SPECIES) == SPECIES_DRAGAPULT)
|
||||
{
|
||||
template.tileTag = ANIM_TAG_DREEPY;
|
||||
if (IsMonShiny(&party[gBattlerPartyIndexes[gBattleAnimAttacker]]) == TRUE)
|
||||
template.paletteTag = ANIM_TAG_DREEPY_SHINY;
|
||||
else
|
||||
template.paletteTag = ANIM_TAG_DREEPY;
|
||||
template.oam = &gOamData_AffineOff_ObjNormal_32x32;
|
||||
if (GetBattlerSide(gBattleAnimAttacker) == B_SIDE_OPPONENT)
|
||||
template.anims = gAnims_DreepyMissileOpponent;
|
||||
else
|
||||
template.anims = gAnims_DreepyMissilePlayer;
|
||||
}
|
||||
else
|
||||
{
|
||||
template.tileTag = ANIM_TAG_AIR_WAVE;
|
||||
template.paletteTag = ANIM_TAG_DREEPY;
|
||||
template.oam = &gOamData_AffineOff_ObjNormal_32x16;
|
||||
if (GetBattlerSide(gBattleAnimAttacker) == B_SIDE_OPPONENT)
|
||||
template.anims = gAnims_DreepyMissileOpponentNotDrag;
|
||||
else
|
||||
template.anims = gAnims_DreepyMissilePlayer;
|
||||
}
|
||||
|
||||
template.images = NULL;
|
||||
template.affineAnims = gDummySpriteAffineAnimTable;
|
||||
template.callback = AnimShadowBall;
|
||||
|
||||
if (CreateSpriteAndAnimate(&template,
|
||||
GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_X_2),
|
||||
GetBattlerSpriteCoord(gBattleAnimTarget, BATTLER_COORD_Y_PIC_OFFSET),
|
||||
subpriority) != MAX_SPRITES) // Don't increment the task count if the sprite couldn't be created(i.e. there are too many created sprites atm).
|
||||
gAnimVisualTaskCount++;
|
||||
}
|
||||
|
||||
// battle_anim_throw.c
|
||||
|
||||
@@ -979,7 +979,7 @@ void AllocateMonSpritesGfx(void)
|
||||
for (i = 0; i < MAX_BATTLERS_COUNT; ++i)
|
||||
{
|
||||
gMonSpritesGfxPtr->sprites[i] = gMonSpritesGfxPtr->firstDecompressed + (i * 0x2000);
|
||||
*(gMonSpritesGfxPtr->templates + i) = gSpriteTemplates_Battlers[i];
|
||||
*(gMonSpritesGfxPtr->templates + i) = gBattlerSpriteTemplates[i];
|
||||
|
||||
for (j = 0; j < 4; ++j)
|
||||
{
|
||||
|
||||
@@ -70,6 +70,7 @@ static void CB2_EndLinkBattle(void);
|
||||
static void EndLinkBattleInSteps(void);
|
||||
static void SpriteCB_MoveWildMonToRight(struct Sprite *sprite);
|
||||
static void SpriteCB_WildMonShowHealthbox(struct Sprite *sprite);
|
||||
static void SpriteCB_WildMonAnimate(struct Sprite *sprite);
|
||||
static void SpriteCB_AnimFaintOpponent(struct Sprite *sprite);
|
||||
static void SpriteCB_BlinkVisible(struct Sprite *sprite);
|
||||
static void oac_poke_ally_(struct Sprite *sprite);
|
||||
@@ -1902,7 +1903,7 @@ static void SpriteCB_WildMonShowHealthbox(struct Sprite *sprite)
|
||||
{
|
||||
StartHealthboxSlideIn(sprite->sBattler);
|
||||
SetHealthboxSpriteVisible(gHealthboxSpriteIds[sprite->sBattler]);
|
||||
sprite->callback = SpriteCallbackDummy_2;
|
||||
sprite->callback = SpriteCB_WildMonAnimate;
|
||||
StartSpriteAnimIfDifferent(sprite, 0);
|
||||
if (WILD_DOUBLE_BATTLE)
|
||||
BeginNormalPaletteFade((0x10000 << sprite->sBattler) | (0x10000 << BATTLE_PARTNER(sprite->sBattler)), 0, 10, 0, RGB(8, 8, 8));
|
||||
@@ -1911,6 +1912,14 @@ static void SpriteCB_WildMonShowHealthbox(struct Sprite *sprite)
|
||||
}
|
||||
}
|
||||
|
||||
static void SpriteCB_WildMonAnimate(struct Sprite *sprite)
|
||||
{
|
||||
if (!gPaletteFade.active)
|
||||
{
|
||||
BattleAnimateFrontSprite(sprite, sprite->sSpeciesId, FALSE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteCallbackDummy_2(struct Sprite *sprite)
|
||||
{
|
||||
}
|
||||
@@ -1990,6 +1999,19 @@ void SpriteCB_HideAsMoveTarget(struct Sprite *sprite)
|
||||
sprite->callback = SpriteCallbackDummy_2;
|
||||
}
|
||||
|
||||
void SpriteCB_OpponentMonFromBall(struct Sprite *sprite)
|
||||
{
|
||||
if (sprite->affineAnimEnded)
|
||||
{
|
||||
if (!(gHitMarker & HITMARKER_NO_ANIMATIONS) || gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))
|
||||
{
|
||||
if (HasTwoFramesAnimation(sprite->sSpeciesId))
|
||||
StartSpriteAnim(sprite, 1);
|
||||
}
|
||||
BattleAnimateFrontSprite(sprite, sprite->sSpeciesId, TRUE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteCB_AllyMon(struct Sprite *sprite)
|
||||
{
|
||||
sprite->callback = oac_poke_ally_;
|
||||
@@ -2119,6 +2141,12 @@ static void SpriteCB_BounceEffect(struct Sprite *sprite)
|
||||
sprite->sSinIndex = (sprite->sSinIndex + sprite->sDelta) & 0xFF;
|
||||
}
|
||||
|
||||
void SpriteCB_PlayerMonFromBall(struct Sprite *sprite)
|
||||
{
|
||||
if (sprite->affineAnimEnded)
|
||||
BattleAnimateBackSprite(sprite, sprite->sSpeciesId);
|
||||
}
|
||||
|
||||
void SpriteCB_PlayerThrowInit(struct Sprite *sprite)
|
||||
{
|
||||
StartSpriteAnim(sprite, 1);
|
||||
|
||||
@@ -724,7 +724,7 @@ static void Task_NewGameScene(u8 taskId)
|
||||
break;
|
||||
case 1:
|
||||
sOakSpeechResources = AllocZeroed(sizeof(*sOakSpeechResources));
|
||||
CreateMonSpritesGfxManager(1, 1);
|
||||
CreateMonSpritesGfxManager(MON_SPR_GFX_MANAGER_A, MON_SPR_GFX_MODE_NORMAL);
|
||||
break;
|
||||
case 2:
|
||||
SetGpuReg(REG_OFFSET_WIN0H, 0);
|
||||
@@ -1792,7 +1792,7 @@ static void Task_OakSpeech_WaitForFade(u8 taskId)
|
||||
static void Task_OakSpeech_FreeResources(u8 taskId)
|
||||
{
|
||||
FreeAllWindowBuffers();
|
||||
DestroyMonSpritesGfxManager();
|
||||
DestroyMonSpritesGfxManager(MON_SPR_GFX_MANAGER_A);
|
||||
Free(sOakSpeechResources);
|
||||
sOakSpeechResources = NULL;
|
||||
gTextFlags.canABSpeedUpPrint = FALSE;
|
||||
@@ -1899,7 +1899,7 @@ static void CreateNidoranFSprite(u8 taskId)
|
||||
{
|
||||
u8 spriteId;
|
||||
|
||||
LoadSpecialPokePic(MonSpritesGfxManager_GetSpritePtr(0), INTRO_SPECIES, 0, TRUE);
|
||||
LoadSpecialPokePic(MonSpritesGfxManager_GetSpritePtr(MON_SPR_GFX_MANAGER_A, 0), INTRO_SPECIES, 0, TRUE);
|
||||
LoadCompressedSpritePaletteUsingHeapWithTag(GetMonSpritePalFromSpeciesAndPersonality(INTRO_SPECIES, 0, 0), INTRO_SPECIES);
|
||||
SetMultiuseSpriteTemplateToPokemon(INTRO_SPECIES, 0);
|
||||
spriteId = CreateSprite(&gMultiuseSpriteTemplate, 96, 96, 1);
|
||||
|
||||
@@ -1029,6 +1029,11 @@ static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite)
|
||||
}
|
||||
|
||||
StartSpriteAffineAnim(&gSprites[gBattlerSpriteIds[sprite->sBattler]], BATTLER_AFFINE_EMERGE);
|
||||
|
||||
if (GetBattlerSide(sprite->sBattler) == B_SIDE_OPPONENT)
|
||||
gSprites[gBattlerSpriteIds[sprite->sBattler]].callback = SpriteCB_OpponentMonFromBall;
|
||||
else
|
||||
gSprites[gBattlerSpriteIds[sprite->sBattler]].callback = SpriteCB_PlayerMonFromBall;
|
||||
AnimateSprite(&gSprites[gBattlerSpriteIds[sprite->sBattler]]);
|
||||
gSprites[gBattlerSpriteIds[sprite->sBattler]].data[1] = 0x1000;
|
||||
}
|
||||
|
||||
391
src/pokemon.c
391
src/pokemon.c
@@ -22,6 +22,7 @@
|
||||
#include "pokemon_animation.h"
|
||||
#include "m4a.h"
|
||||
#include "pokedex.h"
|
||||
#include "pokemon_summary_screen.h"
|
||||
#include "strings.h"
|
||||
#include "overworld.h"
|
||||
#include "party_menu.h"
|
||||
@@ -59,7 +60,7 @@ EWRAM_DATA u8 gEnemyPartyCount = 0;
|
||||
EWRAM_DATA struct Pokemon gEnemyParty[PARTY_SIZE] = {};
|
||||
EWRAM_DATA struct Pokemon gPlayerParty[PARTY_SIZE] = {};
|
||||
EWRAM_DATA struct SpriteTemplate gMultiuseSpriteTemplate = {0};
|
||||
static EWRAM_DATA struct MonSpritesGfxManager *sMonSpritesGfxManager = NULL;
|
||||
EWRAM_DATA static struct MonSpritesGfxManager *sMonSpritesGfxManagers[MON_SPR_GFX_MANAGERS_COUNT] = {NULL};
|
||||
|
||||
static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, u8 substructType);
|
||||
static bool8 IsPokemonStorageFull(void);
|
||||
@@ -890,7 +891,7 @@ static const u8 sHoldEffectToType[][2] =
|
||||
{HOLD_EFFECT_FAIRY_POWER, TYPE_FAIRY},
|
||||
};
|
||||
|
||||
const struct SpriteTemplate gSpriteTemplates_Battlers[MAX_BATTLERS_COUNT] =
|
||||
const struct SpriteTemplate gBattlerSpriteTemplates[MAX_BATTLERS_COUNT] =
|
||||
{
|
||||
[B_POSITION_PLAYER_LEFT] = {
|
||||
.tileTag = TAG_NONE,
|
||||
@@ -2027,31 +2028,28 @@ u8 GetGenderFromSpeciesAndPersonality(u16 species, u32 personality)
|
||||
void SetMultiuseSpriteTemplateToPokemon(u16 speciesTag, u8 battlerPosition)
|
||||
{
|
||||
if (gMonSpritesGfxPtr != NULL)
|
||||
{
|
||||
if (battlerPosition >= MAX_BATTLERS_COUNT)
|
||||
battlerPosition = 0;
|
||||
|
||||
gMultiuseSpriteTemplate = gMonSpritesGfxPtr->templates[battlerPosition];
|
||||
}
|
||||
else if (sMonSpritesGfxManagers[MON_SPR_GFX_MANAGER_A])
|
||||
gMultiuseSpriteTemplate = sMonSpritesGfxManagers[MON_SPR_GFX_MANAGER_A]->templates[battlerPosition];
|
||||
else if (sMonSpritesGfxManagers[MON_SPR_GFX_MANAGER_B])
|
||||
gMultiuseSpriteTemplate = sMonSpritesGfxManagers[MON_SPR_GFX_MANAGER_B]->templates[battlerPosition];
|
||||
else
|
||||
gMultiuseSpriteTemplate = gBattlerSpriteTemplates[battlerPosition];
|
||||
|
||||
gMultiuseSpriteTemplate.paletteTag = speciesTag;
|
||||
if (battlerPosition == B_POSITION_PLAYER_LEFT || battlerPosition == B_POSITION_PLAYER_RIGHT)
|
||||
gMultiuseSpriteTemplate.anims = gAnims_MonPic;
|
||||
else
|
||||
{
|
||||
if (sMonSpritesGfxManager)
|
||||
{
|
||||
if (battlerPosition >= (s8)sMonSpritesGfxManager->battlePosition) // why a cast?!? changing the unk0_2 type to s8 causes extra shifts, but a cast is the correct fix. why, compiler?
|
||||
battlerPosition = 0;
|
||||
if (speciesTag > SPECIES_SHINY_TAG)
|
||||
speciesTag = speciesTag - SPECIES_SHINY_TAG;
|
||||
|
||||
gMultiuseSpriteTemplate = sMonSpritesGfxManager->templates[battlerPosition];
|
||||
}
|
||||
speciesTag = SanitizeSpeciesId(speciesTag);
|
||||
if (gSpeciesInfo[speciesTag].frontAnimFrames != NULL)
|
||||
gMultiuseSpriteTemplate.anims = gSpeciesInfo[speciesTag].frontAnimFrames;
|
||||
else
|
||||
{
|
||||
if (battlerPosition >= MAX_BATTLERS_COUNT)
|
||||
battlerPosition = 0;
|
||||
|
||||
gMultiuseSpriteTemplate = gSpriteTemplates_Battlers[battlerPosition];
|
||||
}
|
||||
gMultiuseSpriteTemplate.anims = gSpeciesInfo[SPECIES_NONE].frontAnimFrames;
|
||||
}
|
||||
gMultiuseSpriteTemplate.paletteTag = speciesTag;
|
||||
gMultiuseSpriteTemplate.anims = gAnims_MonPic;
|
||||
}
|
||||
|
||||
void SetMultiuseSpriteTemplateToTrainerBack(u16 trainerSpriteId, u8 battlerPosition)
|
||||
@@ -2067,7 +2065,7 @@ void SetMultiuseSpriteTemplateToTrainerBack(u16 trainerSpriteId, u8 battlerPosit
|
||||
if (gMonSpritesGfxPtr != NULL)
|
||||
gMultiuseSpriteTemplate = gMonSpritesGfxPtr->templates[battlerPosition];
|
||||
else
|
||||
gMultiuseSpriteTemplate = gSpriteTemplates_Battlers[battlerPosition];
|
||||
gMultiuseSpriteTemplate = gBattlerSpriteTemplates[battlerPosition];
|
||||
gMultiuseSpriteTemplate.anims = gTrainerFrontAnimsPtrTable[trainerSpriteId];
|
||||
}
|
||||
}
|
||||
@@ -2077,7 +2075,7 @@ void SetMultiuseSpriteTemplateToTrainerFront(u16 trainerPicId, u8 battlerPositio
|
||||
if (gMonSpritesGfxPtr != NULL)
|
||||
gMultiuseSpriteTemplate = gMonSpritesGfxPtr->templates[battlerPosition];
|
||||
else
|
||||
gMultiuseSpriteTemplate = gSpriteTemplates_Battlers[battlerPosition];
|
||||
gMultiuseSpriteTemplate = gBattlerSpriteTemplates[battlerPosition];
|
||||
|
||||
gMultiuseSpriteTemplate.paletteTag = trainerPicId;
|
||||
gMultiuseSpriteTemplate.anims = gTrainerFrontAnimsPtrTable[trainerPicId];
|
||||
@@ -5612,6 +5610,135 @@ u8 *GetTrainerPartnerName(void)
|
||||
return gLinkPlayers[GetBattlerMultiplayerId(gLinkPlayers[id].id ^ 2)].name;
|
||||
}
|
||||
|
||||
#define READ_PTR_FROM_TASK(taskId, dataId) \
|
||||
(void *)( \
|
||||
((u16)(gTasks[taskId].data[dataId]) | \
|
||||
((u16)(gTasks[taskId].data[dataId + 1]) << 16)))
|
||||
|
||||
#define STORE_PTR_IN_TASK(ptr, taskId, dataId) \
|
||||
{ \
|
||||
gTasks[taskId].data[dataId] = (u32)(ptr); \
|
||||
gTasks[taskId].data[dataId + 1] = (u32)(ptr) >> 16; \
|
||||
}
|
||||
|
||||
#define sAnimId data[2]
|
||||
#define sAnimDelay data[3]
|
||||
|
||||
static void Task_AnimateAfterDelay(u8 taskId)
|
||||
{
|
||||
if (--gTasks[taskId].sAnimDelay == 0)
|
||||
{
|
||||
LaunchAnimationTaskForFrontSprite(READ_PTR_FROM_TASK(taskId, 0), gTasks[taskId].sAnimId);
|
||||
DestroyTask(taskId);
|
||||
}
|
||||
}
|
||||
|
||||
// static void Task_PokemonSummaryAnimateAfterDelay(u8 taskId)
|
||||
// {
|
||||
// if (--gTasks[taskId].sAnimDelay == 0)
|
||||
// {
|
||||
// StartMonSummaryAnimation(READ_PTR_FROM_TASK(taskId, 0), gTasks[taskId].sAnimId);
|
||||
// SummaryScreen_SetAnimDelayTaskId(TASK_NONE);
|
||||
// DestroyTask(taskId);
|
||||
// }
|
||||
// }
|
||||
|
||||
void BattleAnimateFrontSprite(struct Sprite *sprite, u16 species, bool8 noCry, u8 panMode)
|
||||
{
|
||||
if (gHitMarker & HITMARKER_NO_ANIMATIONS && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)))
|
||||
DoMonFrontSpriteAnimation(sprite, species, noCry, panMode | SKIP_FRONT_ANIM);
|
||||
else
|
||||
DoMonFrontSpriteAnimation(sprite, species, noCry, panMode);
|
||||
}
|
||||
|
||||
void DoMonFrontSpriteAnimation(struct Sprite *sprite, u16 species, bool8 noCry, u8 panModeAnimFlag)
|
||||
{
|
||||
s8 pan;
|
||||
switch (panModeAnimFlag & (u8)~SKIP_FRONT_ANIM) // Exclude anim flag to get pan mode
|
||||
{
|
||||
case 0:
|
||||
pan = -25;
|
||||
break;
|
||||
case 1:
|
||||
pan = 25;
|
||||
break;
|
||||
default:
|
||||
pan = 0;
|
||||
break;
|
||||
}
|
||||
if (panModeAnimFlag & SKIP_FRONT_ANIM)
|
||||
{
|
||||
// No animation, only check if cry needs to be played
|
||||
if (!noCry)
|
||||
PlayCry_Normal(species, pan);
|
||||
sprite->callback = SpriteCallbackDummy;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!noCry)
|
||||
{
|
||||
PlayCry_Normal(species, pan);
|
||||
if (HasTwoFramesAnimation(species))
|
||||
StartSpriteAnim(sprite, 1);
|
||||
}
|
||||
if (gSpeciesInfo[species].frontAnimDelay != 0)
|
||||
{
|
||||
// Animation has delay, start delay task
|
||||
u8 taskId = CreateTask(Task_AnimateAfterDelay, 0);
|
||||
STORE_PTR_IN_TASK(sprite, taskId, 0);
|
||||
gTasks[taskId].sAnimId = gSpeciesInfo[species].frontAnimId;
|
||||
gTasks[taskId].sAnimDelay = gSpeciesInfo[species].frontAnimDelay;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No delay, start animation
|
||||
LaunchAnimationTaskForFrontSprite(sprite, gSpeciesInfo[species].frontAnimId);
|
||||
}
|
||||
sprite->callback = SpriteCallbackDummy_2;
|
||||
}
|
||||
}
|
||||
|
||||
// void PokemonSummaryDoMonAnimation(struct Sprite *sprite, u16 species, bool8 oneFrame)
|
||||
// {
|
||||
// if (!oneFrame && HasTwoFramesAnimation(species))
|
||||
// StartSpriteAnim(sprite, 1);
|
||||
// if (gSpeciesInfo[species].frontAnimDelay != 0)
|
||||
// {
|
||||
// // Animation has delay, start delay task
|
||||
// u8 taskId = CreateTask(Task_PokemonSummaryAnimateAfterDelay, 0);
|
||||
// STORE_PTR_IN_TASK(sprite, taskId, 0);
|
||||
// gTasks[taskId].sAnimId = gSpeciesInfo[species].frontAnimId;
|
||||
// gTasks[taskId].sAnimDelay = gSpeciesInfo[species].frontAnimDelay;
|
||||
// SummaryScreen_SetAnimDelayTaskId(taskId);
|
||||
// SetSpriteCB_MonAnimDummy(sprite);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // No delay, start animation
|
||||
// StartMonSummaryAnimation(sprite, gSpeciesInfo[species].frontAnimId);
|
||||
// }
|
||||
// }
|
||||
|
||||
// void StopPokemonAnimationDelayTask(void)
|
||||
// {
|
||||
// u8 delayTaskId = FindTaskIdByFunc(Task_PokemonSummaryAnimateAfterDelay);
|
||||
// if (delayTaskId != TASK_NONE)
|
||||
// DestroyTask(delayTaskId);
|
||||
// }
|
||||
|
||||
void BattleAnimateBackSprite(struct Sprite *sprite, u16 species)
|
||||
{
|
||||
if (gHitMarker & HITMARKER_NO_ANIMATIONS && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)))
|
||||
{
|
||||
sprite->callback = SpriteCallbackDummy;
|
||||
}
|
||||
else
|
||||
{
|
||||
LaunchAnimationTaskForBackSprite(sprite, GetSpeciesBackAnimSet(species));
|
||||
sprite->callback = SpriteCallbackDummy_2;
|
||||
}
|
||||
}
|
||||
|
||||
u8 GetPlayerPartyHighestLevel(void)
|
||||
{
|
||||
s32 slot;
|
||||
@@ -5704,6 +5831,11 @@ void HandleSetPokedexFlag(u16 nationalNum, u8 caseId, u32 personality)
|
||||
}
|
||||
}
|
||||
|
||||
bool32 HasTwoFramesAnimation(u16 species)
|
||||
{
|
||||
return P_TWO_FRAME_FRONT_SPRITES && species != SPECIES_UNOWN;
|
||||
}
|
||||
|
||||
bool8 CheckBattleTypeGhost(struct Pokemon *mon, u8 battlerId)
|
||||
{
|
||||
u8 nickname[POKEMON_NAME_LENGTH + 1];
|
||||
@@ -5722,131 +5854,100 @@ bool8 CheckBattleTypeGhost(struct Pokemon *mon, u8 battlerId)
|
||||
#define ALLOC_FAIL_STRUCT (1 << 1)
|
||||
#define GFX_MANAGER_ACTIVE 0xA3 // Arbitrary value
|
||||
|
||||
static void InitMonSpritesGfx_Mode1(struct MonSpritesGfxManager *structPtr, u8 battlePosition)
|
||||
{
|
||||
u16 i = 0, j = 0;
|
||||
|
||||
if (battlePosition >= MAX_BATTLERS_COUNT)
|
||||
{
|
||||
for (i = 0; i < (s8)structPtr->numSprites; ++i)
|
||||
{
|
||||
structPtr->templates[i] = gSpriteTemplates_Battlers[i];
|
||||
for (j = 0; j < structPtr->numFrames; ++j)
|
||||
structPtr->frameImages[i * structPtr->numFrames + j].data = &structPtr->spritePointers[i][j * MON_PIC_SIZE];
|
||||
structPtr->templates[i].images = &structPtr->frameImages[i * structPtr->numFrames];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const struct SpriteTemplate *template = &gSpriteTemplates_Battlers[battlePosition];
|
||||
|
||||
structPtr->templates[0] = *template;
|
||||
for (j = 0; j < structPtr->numFrames; ++j)
|
||||
structPtr->frameImages[j].data = &structPtr->spritePointers[0][j * MON_PIC_SIZE];
|
||||
structPtr->templates[0].images = structPtr->frameImages;
|
||||
}
|
||||
}
|
||||
|
||||
// not used
|
||||
static void InitMonSpritesGfx_Mode0(struct MonSpritesGfxManager *structPtr)
|
||||
static void InitMonSpritesGfx_Battle(struct MonSpritesGfxManager* gfx)
|
||||
{
|
||||
u16 i, j;
|
||||
|
||||
for (i = 0; i < (s8)structPtr->numSprites; ++i)
|
||||
for (i = 0; i < gfx->numSprites; i++)
|
||||
{
|
||||
structPtr->templates[i] = sSpriteTemplate_64x64;
|
||||
for (j = 0; j < structPtr->numFrames; ++j)
|
||||
structPtr->frameImages[i * structPtr->numSprites + j].data = &structPtr->spritePointers[i][j * MON_PIC_SIZE];
|
||||
structPtr->templates[i].images = &structPtr->frameImages[i * structPtr->numSprites]; // should be numFrames logically
|
||||
structPtr->templates[i].anims = gAnims_MonPic;
|
||||
structPtr->templates[i].paletteTag = i;
|
||||
gfx->templates[i] = gBattlerSpriteTemplates[i];
|
||||
for (j = 0; j < gfx->numFrames; j++)
|
||||
gfx->frameImages[i * gfx->numFrames + j].data = &gfx->spritePointers[i][j * MON_PIC_SIZE];
|
||||
|
||||
gfx->templates[i].images = &gfx->frameImages[i * gfx->numFrames];
|
||||
}
|
||||
}
|
||||
|
||||
struct MonSpritesGfxManager *CreateMonSpritesGfxManager(u8 battlePosition, u8 mode)
|
||||
static void InitMonSpritesGfx_FullParty(struct MonSpritesGfxManager* gfx)
|
||||
{
|
||||
s32 size;
|
||||
u8 i, failureFlags = 0;
|
||||
|
||||
if (sMonSpritesGfxManager != NULL)
|
||||
u16 i, j;
|
||||
for (i = 0; i < gfx->numSprites; i++)
|
||||
{
|
||||
if (sMonSpritesGfxManager->active == GFX_MANAGER_ACTIVE)
|
||||
return NULL;
|
||||
memset(sMonSpritesGfxManager, 0, sizeof(*sMonSpritesGfxManager));
|
||||
sMonSpritesGfxManager = NULL;
|
||||
gfx->templates[i] = sSpriteTemplate_64x64;
|
||||
for (j = 0; j < gfx->numFrames; j++)
|
||||
gfx->frameImages[i * gfx->numSprites + j].data = &gfx->spritePointers[i][j * MON_PIC_SIZE];
|
||||
|
||||
gfx->templates[i].images = &gfx->frameImages[i * gfx->numSprites];
|
||||
gfx->templates[i].anims = gAnims_MonPic;
|
||||
gfx->templates[i].paletteTag = i;
|
||||
}
|
||||
sMonSpritesGfxManager = AllocZeroed(sizeof(*sMonSpritesGfxManager));
|
||||
if (sMonSpritesGfxManager == NULL)
|
||||
}
|
||||
|
||||
struct MonSpritesGfxManager *CreateMonSpritesGfxManager(u8 managerId, u8 mode)
|
||||
{
|
||||
u8 i;
|
||||
u8 failureFlags;
|
||||
struct MonSpritesGfxManager *gfx;
|
||||
|
||||
failureFlags = 0;
|
||||
managerId %= MON_SPR_GFX_MANAGERS_COUNT;
|
||||
gfx = AllocZeroed(sizeof(*gfx));
|
||||
if (gfx == NULL)
|
||||
return NULL;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case 1:
|
||||
if (battlePosition == MAX_BATTLERS_COUNT)
|
||||
{
|
||||
sMonSpritesGfxManager->numSprites = MAX_BATTLERS_COUNT;
|
||||
sMonSpritesGfxManager->battlePosition = MAX_BATTLERS_COUNT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (battlePosition > MAX_BATTLERS_COUNT)
|
||||
battlePosition = 0;
|
||||
sMonSpritesGfxManager->numSprites = 1;
|
||||
sMonSpritesGfxManager->battlePosition = 1;
|
||||
}
|
||||
sMonSpritesGfxManager->numFrames = MAX_MON_PIC_FRAMES;
|
||||
sMonSpritesGfxManager->mode = 1;
|
||||
case MON_SPR_GFX_MODE_FULL_PARTY:
|
||||
gfx->numSprites = PARTY_SIZE + 1;
|
||||
gfx->numSprites2 = PARTY_SIZE + 1;
|
||||
gfx->numFrames = MAX_MON_PIC_FRAMES;
|
||||
gfx->dataSize = 1;
|
||||
gfx->mode = MON_SPR_GFX_MODE_FULL_PARTY;
|
||||
break;
|
||||
case 0:
|
||||
// case MON_SPR_GFX_MODE_BATTLE:
|
||||
case MON_SPR_GFX_MODE_NORMAL:
|
||||
default:
|
||||
if (!battlePosition)
|
||||
battlePosition = 1;
|
||||
if (battlePosition > 8)
|
||||
battlePosition = 8;
|
||||
sMonSpritesGfxManager->numSprites = (battlePosition << 16) >> 16;
|
||||
sMonSpritesGfxManager->battlePosition = battlePosition;
|
||||
sMonSpritesGfxManager->numFrames = 4;
|
||||
sMonSpritesGfxManager->mode = 0;
|
||||
gfx->numSprites = MAX_BATTLERS_COUNT;
|
||||
gfx->numSprites2 = MAX_BATTLERS_COUNT;
|
||||
gfx->numFrames = MAX_MON_PIC_FRAMES;
|
||||
gfx->dataSize = 1;
|
||||
gfx->mode = MON_SPR_GFX_MODE_NORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
// Set up sprite / sprite pointer buffers
|
||||
size = sMonSpritesGfxManager->numFrames * MON_PIC_SIZE;
|
||||
sMonSpritesGfxManager->dataSize = size;
|
||||
sMonSpritesGfxManager->spriteBuffer = AllocZeroed(sMonSpritesGfxManager->numSprites * size);
|
||||
sMonSpritesGfxManager->spritePointers = AllocZeroed(sMonSpritesGfxManager->numSprites * 32); // ? Only * 4 is necessary, perhaps they were thinking bits.
|
||||
if (sMonSpritesGfxManager->spriteBuffer == NULL || sMonSpritesGfxManager->spritePointers == NULL)
|
||||
gfx->spriteBuffer = AllocZeroed(gfx->dataSize * MON_PIC_SIZE * MAX_MON_PIC_FRAMES * gfx->numSprites);
|
||||
gfx->spritePointers = AllocZeroed(gfx->numSprites * 32); // ? Only * 4 is necessary, perhaps they were thinking bits.
|
||||
if (gfx->spriteBuffer == NULL || gfx->spritePointers == NULL)
|
||||
{
|
||||
failureFlags |= ALLOC_FAIL_BUFFER;
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
for (i = 0; i < (s8)sMonSpritesGfxManager->numSprites; ++i)
|
||||
sMonSpritesGfxManager->spritePointers[i] = &sMonSpritesGfxManager->spriteBuffer[sMonSpritesGfxManager->dataSize * i];
|
||||
} while (0);
|
||||
for (i = 0; i < gfx->numSprites; i++)
|
||||
gfx->spritePointers[i] = gfx->spriteBuffer + (gfx->dataSize * MON_PIC_SIZE * MAX_MON_PIC_FRAMES * i);
|
||||
}
|
||||
|
||||
// Set up sprite structs
|
||||
sMonSpritesGfxManager->templates = AllocZeroed(sizeof(struct SpriteTemplate) * sMonSpritesGfxManager->numSprites);
|
||||
sMonSpritesGfxManager->frameImages = AllocZeroed(sMonSpritesGfxManager->numSprites * sizeof(struct SpriteFrameImage) * sMonSpritesGfxManager->numFrames);
|
||||
if (sMonSpritesGfxManager->templates == NULL || sMonSpritesGfxManager->frameImages == NULL)
|
||||
gfx->templates = AllocZeroed(sizeof(struct SpriteTemplate) * gfx->numSprites);
|
||||
gfx->frameImages = AllocZeroed(sizeof(struct SpriteFrameImage) * gfx->numSprites * gfx->numFrames);
|
||||
if (gfx->templates == NULL || gfx->frameImages == NULL)
|
||||
{
|
||||
failureFlags |= ALLOC_FAIL_STRUCT;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < sMonSpritesGfxManager->numFrames * sMonSpritesGfxManager->numSprites; i++)
|
||||
sMonSpritesGfxManager->frameImages[i].size = MON_PIC_SIZE;
|
||||
switch (sMonSpritesGfxManager->mode)
|
||||
for (i = 0; i < gfx->numFrames * gfx->numSprites; i++)
|
||||
gfx->frameImages[i].size = MON_PIC_SIZE;
|
||||
|
||||
switch (gfx->mode)
|
||||
{
|
||||
case 1:
|
||||
InitMonSpritesGfx_Mode1(sMonSpritesGfxManager, battlePosition);
|
||||
case MON_SPR_GFX_MODE_FULL_PARTY:
|
||||
InitMonSpritesGfx_FullParty(gfx);
|
||||
break;
|
||||
case 0:
|
||||
case MON_SPR_GFX_MODE_NORMAL:
|
||||
case MON_SPR_GFX_MODE_BATTLE:
|
||||
default:
|
||||
InitMonSpritesGfx_Mode0(sMonSpritesGfxManager);
|
||||
InitMonSpritesGfx_Battle(gfx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -5854,61 +5955,67 @@ struct MonSpritesGfxManager *CreateMonSpritesGfxManager(u8 battlePosition, u8 mo
|
||||
// If either of the allocations failed free their respective members
|
||||
if (failureFlags & ALLOC_FAIL_STRUCT)
|
||||
{
|
||||
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->frameImages);
|
||||
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->templates);
|
||||
TRY_FREE_AND_SET_NULL(gfx->frameImages);
|
||||
TRY_FREE_AND_SET_NULL(gfx->templates);
|
||||
}
|
||||
if (failureFlags & ALLOC_FAIL_BUFFER)
|
||||
{
|
||||
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->spritePointers);
|
||||
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->spriteBuffer);
|
||||
TRY_FREE_AND_SET_NULL(gfx->spritePointers);
|
||||
TRY_FREE_AND_SET_NULL(gfx->spriteBuffer);
|
||||
}
|
||||
|
||||
if (failureFlags)
|
||||
{
|
||||
// Clear, something failed to allocate
|
||||
memset(sMonSpritesGfxManager, 0, sizeof(*sMonSpritesGfxManager));
|
||||
FREE_AND_SET_NULL(sMonSpritesGfxManager);
|
||||
memset(gfx, 0, sizeof(*gfx));
|
||||
Free(gfx);
|
||||
}
|
||||
else
|
||||
{
|
||||
sMonSpritesGfxManager->active = GFX_MANAGER_ACTIVE;
|
||||
gfx->active = GFX_MANAGER_ACTIVE;
|
||||
sMonSpritesGfxManagers[managerId] = gfx;
|
||||
}
|
||||
return sMonSpritesGfxManager;
|
||||
|
||||
return sMonSpritesGfxManagers[managerId];
|
||||
}
|
||||
|
||||
void DestroyMonSpritesGfxManager(void)
|
||||
void DestroyMonSpritesGfxManager(u8 managerId)
|
||||
{
|
||||
if (sMonSpritesGfxManager != NULL)
|
||||
struct MonSpritesGfxManager *gfx;
|
||||
|
||||
managerId %= MON_SPR_GFX_MANAGERS_COUNT;
|
||||
gfx = sMonSpritesGfxManagers[managerId];
|
||||
if (gfx == NULL)
|
||||
return;
|
||||
|
||||
if (gfx->active != GFX_MANAGER_ACTIVE)
|
||||
{
|
||||
if (sMonSpritesGfxManager->active != GFX_MANAGER_ACTIVE)
|
||||
{
|
||||
memset(sMonSpritesGfxManager, 0, sizeof(*sMonSpritesGfxManager));
|
||||
sMonSpritesGfxManager = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->frameImages);
|
||||
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->templates);
|
||||
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->spritePointers);
|
||||
TRY_FREE_AND_SET_NULL(sMonSpritesGfxManager->spriteBuffer);
|
||||
memset(sMonSpritesGfxManager, 0, sizeof(*sMonSpritesGfxManager));
|
||||
FREE_AND_SET_NULL(sMonSpritesGfxManager);
|
||||
}
|
||||
|
||||
memset(gfx, 0, sizeof(*gfx));
|
||||
}
|
||||
else
|
||||
{
|
||||
TRY_FREE_AND_SET_NULL(gfx->frameImages);
|
||||
TRY_FREE_AND_SET_NULL(gfx->templates);
|
||||
TRY_FREE_AND_SET_NULL(gfx->spritePointers);
|
||||
TRY_FREE_AND_SET_NULL(gfx->spriteBuffer);
|
||||
memset(gfx, 0, sizeof(*gfx));
|
||||
Free(gfx);
|
||||
}
|
||||
}
|
||||
|
||||
u8 *MonSpritesGfxManager_GetSpritePtr(u8 spriteNum)
|
||||
u8 *MonSpritesGfxManager_GetSpritePtr(u8 managerId, u8 spriteNum)
|
||||
{
|
||||
if (sMonSpritesGfxManager->active != GFX_MANAGER_ACTIVE)
|
||||
struct MonSpritesGfxManager *gfx = sMonSpritesGfxManagers[managerId % MON_SPR_GFX_MANAGERS_COUNT];
|
||||
if (gfx->active != GFX_MANAGER_ACTIVE)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spriteNum >= (s8)sMonSpritesGfxManager->numSprites)
|
||||
if (spriteNum >= gfx->numSprites)
|
||||
spriteNum = 0;
|
||||
return sMonSpritesGfxManager->spritePointers[spriteNum];
|
||||
|
||||
return gfx->spritePointers[spriteNum];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
5196
src/pokemon_animation.c
Normal file
5196
src/pokemon_animation.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1437,14 +1437,14 @@ static void UpdateSubmenuTwoOptionValue(u8 taskId, bool8 increment)
|
||||
#define sAnimId data[2]
|
||||
#define sAnimDelay data[3]
|
||||
|
||||
// static void Task_AnimateAfterDelay(u8 taskId)
|
||||
// {
|
||||
// if (--gTasks[taskId].sAnimDelay == 0)
|
||||
// {
|
||||
// LaunchAnimationTaskForFrontSprite(READ_PTR_FROM_TASK(taskId, 0), gTasks[taskId].sAnimId);
|
||||
// DestroyTask(taskId);
|
||||
// }
|
||||
// }
|
||||
static void Task_AnimateAfterDelay(u8 taskId)
|
||||
{
|
||||
if (--gTasks[taskId].sAnimDelay == 0)
|
||||
{
|
||||
LaunchAnimationTaskForFrontSprite(READ_PTR_FROM_TASK(taskId, 0), gTasks[taskId].sAnimId);
|
||||
DestroyTask(taskId);
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleInput_PokemonSpriteVisualizer(u8 taskId)
|
||||
{
|
||||
@@ -1460,22 +1460,22 @@ static void HandleInput_PokemonSpriteVisualizer(u8 taskId)
|
||||
if (JOY_NEW(R_BUTTON) && (Frontsprite->callback == SpriteCallbackDummy))
|
||||
{
|
||||
PlayCryInternal(data->currentmonId, 0, 120, 10, 0);
|
||||
// if (HasTwoFramesAnimation(data->currentmonId))
|
||||
// StartSpriteAnim(Frontsprite, 1);
|
||||
if (HasTwoFramesAnimation(data->currentmonId))
|
||||
StartSpriteAnim(Frontsprite, 1);
|
||||
|
||||
// if (gSpeciesInfo[data->currentmonId].frontAnimDelay != 0)
|
||||
// {
|
||||
// // Animation has delay, start delay task
|
||||
// u8 taskId = CreateTask(Task_AnimateAfterDelay, 0);
|
||||
// STORE_PTR_IN_TASK(Frontsprite, taskId, 0);
|
||||
// gTasks[taskId].sAnimId = data->animIdFront;
|
||||
// gTasks[taskId].sAnimDelay = gSpeciesInfo[data->currentmonId].frontAnimDelay;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // No delay, start animation
|
||||
// LaunchAnimationTaskForFrontSprite(Frontsprite, data->animIdFront);
|
||||
// }
|
||||
if (gSpeciesInfo[data->currentmonId].frontAnimDelay != 0)
|
||||
{
|
||||
// Animation has delay, start delay task
|
||||
u8 taskId = CreateTask(Task_AnimateAfterDelay, 0);
|
||||
STORE_PTR_IN_TASK(Frontsprite, taskId, 0);
|
||||
gTasks[taskId].sAnimId = data->animIdFront;
|
||||
gTasks[taskId].sAnimDelay = gSpeciesInfo[data->currentmonId].frontAnimDelay;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No delay, start animation
|
||||
LaunchAnimationTaskForFrontSprite(Frontsprite, data->animIdFront);
|
||||
}
|
||||
}
|
||||
|
||||
if (JOY_NEW(START_BUTTON))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "gflib.h"
|
||||
#include "decompress.h"
|
||||
#include "data.h"
|
||||
#include "trainer_pokemon_sprites.h"
|
||||
|
||||
struct PicData
|
||||
{
|
||||
@@ -25,6 +26,13 @@ static const struct OamData sOamData_Normal =
|
||||
.size = SPRITE_SIZE(64x64)
|
||||
};
|
||||
|
||||
static const struct OamData sOamData_Affine =
|
||||
{
|
||||
.affineMode = ST_OAM_AFFINE_NORMAL,
|
||||
.shape = SPRITE_SHAPE(64x64),
|
||||
.size = SPRITE_SIZE(64x64)
|
||||
};
|
||||
|
||||
void DummyPicSpriteCallback(struct Sprite *sprite)
|
||||
{
|
||||
|
||||
@@ -156,6 +164,84 @@ u16 CreatePicSprite(u16 species, bool32 isShiny, u32 personality, bool8 isFrontP
|
||||
return spriteId;
|
||||
}
|
||||
|
||||
u16 CreateMonPicSprite_Affine(u16 species, bool8 isShiny, u32 personality, u8 flags, s16 x, s16 y, u8 paletteSlot, u16 paletteTag)
|
||||
{
|
||||
u8 *framePics;
|
||||
struct SpriteFrameImage *images;
|
||||
int j;
|
||||
u8 i;
|
||||
u8 spriteId;
|
||||
u8 type;
|
||||
species = SanitizeSpeciesId(species);
|
||||
|
||||
for (i = 0; i < PICS_COUNT; i++)
|
||||
{
|
||||
if (!sSpritePics[i].active)
|
||||
break;
|
||||
}
|
||||
if (i == PICS_COUNT)
|
||||
return 0xFFFF;
|
||||
|
||||
framePics = Alloc(MON_PIC_SIZE * MAX_MON_PIC_FRAMES);
|
||||
if (!framePics)
|
||||
return 0xFFFF;
|
||||
|
||||
if (flags & F_MON_PIC_NO_AFFINE)
|
||||
{
|
||||
flags &= ~F_MON_PIC_NO_AFFINE;
|
||||
type = MON_PIC_AFFINE_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = flags;
|
||||
}
|
||||
images = Alloc(sizeof(struct SpriteFrameImage) * MAX_MON_PIC_FRAMES);
|
||||
if (!images)
|
||||
{
|
||||
Free(framePics);
|
||||
return 0xFFFF;
|
||||
}
|
||||
if (DecompressPic(species, personality, flags, framePics, FALSE))
|
||||
{
|
||||
// debug trap?
|
||||
return 0xFFFF;
|
||||
}
|
||||
for (j = 0; j < MAX_MON_PIC_FRAMES; j ++)
|
||||
{
|
||||
images[j].data = framePics + MON_PIC_SIZE * j;
|
||||
images[j].size = MON_PIC_SIZE;
|
||||
}
|
||||
sCreatingSpriteTemplate.tileTag = TAG_NONE;
|
||||
sCreatingSpriteTemplate.anims = gSpeciesInfo[species].frontAnimFrames;
|
||||
sCreatingSpriteTemplate.images = images;
|
||||
if (type == MON_PIC_AFFINE_FRONT)
|
||||
{
|
||||
sCreatingSpriteTemplate.affineAnims = gAffineAnims_BattleSpriteOpponentSide;
|
||||
sCreatingSpriteTemplate.oam = &sOamData_Affine;
|
||||
}
|
||||
else if (type == MON_PIC_AFFINE_BACK)
|
||||
{
|
||||
sCreatingSpriteTemplate.affineAnims = gAffineAnims_BattleSpritePlayerSide;
|
||||
sCreatingSpriteTemplate.oam = &sOamData_Affine;
|
||||
}
|
||||
else // MON_PIC_AFFINE_NONE
|
||||
{
|
||||
sCreatingSpriteTemplate.oam = &sOamData_Normal;
|
||||
sCreatingSpriteTemplate.affineAnims = gDummySpriteAffineAnimTable;
|
||||
}
|
||||
sCreatingSpriteTemplate.callback = DummyPicSpriteCallback;
|
||||
LoadPicPaletteByTagOrSlot(species, isShiny, personality, paletteSlot, paletteTag, FALSE);
|
||||
spriteId = CreateSprite(&sCreatingSpriteTemplate, x, y, 0);
|
||||
if (paletteTag == TAG_NONE)
|
||||
gSprites[spriteId].oam.paletteNum = paletteSlot;
|
||||
sSpritePics[i].frames = framePics;
|
||||
sSpritePics[i].images = images;
|
||||
sSpritePics[i].paletteTag = paletteTag;
|
||||
sSpritePics[i].spriteId = spriteId;
|
||||
sSpritePics[i].active = TRUE;
|
||||
return spriteId;
|
||||
}
|
||||
|
||||
u16 FreeAndDestroyPicSpriteInternal(u16 spriteId)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
Reference in New Issue
Block a user