Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
psytp2 committed Sep 10, 2024
2 parents cb8404f + b478881 commit f48833e
Show file tree
Hide file tree
Showing 34 changed files with 2,012 additions and 252 deletions.
10 changes: 10 additions & 0 deletions docs/ai_flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ If you are not using competitive syntax parties, instead access the trainer data
# What AI Flags does pokeemerald-expansion have?
This section lists all of expansion’s AI Flags and briefly describes the effect they have on the AI’s behaviour. In all cases, please check the corresponding function or surrounding code around their implementation for more details. Some of these functions are vanilla, some share a name with vanilla but have been modified to varying degrees, and some are completely new.

## Composite AI Flags

Expansion has two "composite" AI flags, `AI_FLAG_BASIC_TRAINER` and `AI_FLAG_SMART_TRAINER`. This means that these flags have no unique functionality themselves, and can instead be thought of as groups of other flags that are all enabled when this flag is enabled. The idea behind these flags is that if you don't care to manage the detailed behaviour of a particular trainer, you can use these as a baseline instead, and expansion will keep them updated for you.

`AI_FLAG_BASIC_TRAINER` is expansion's version of generic, normal AI behaviour. It includes `AI_FLAG_CHECK_BAD_MOVE` (don't use bad moves), `AI_FLAG_TRY_TO_FAINT` (faint the player where possible), and `AI_FLAG_CHECK_VIABILITY` (choose the most effective move to use in the current context). Trainers with this flag will still be smarter than they are in vanilla as there have been dramatic improvements made to move selection, but not incredibly so. Trainers with this flag should feel like normal trainers. In general we recommend these three flags be used in all cases, unless you specifically want a trainer who makes obvious mistakes in battle.

`AI_FLAG_SMART_TRAINER` is expansion's version of a "smart AI". It includes everything in `AI_FLAG_BASIC_TRAINER` along with `AI_FLAG_SMART_SWITCHING` (make smart decisions about when to switch), `AI_FLAG_SMART_MON_CHOICES` (make smart decisions about what mon to send in after a switch / KO), and `AI_FLAG_OMNISCIENT` (awareness of what moves, items, and abilities the player's mons have to better inform decisions). Expansion will keep this updated to represent the most objectively intelligent behaviour our flags are capable of producing.

Expansion has LOADS of flags, which will be covered in the rest of this guide. If you don't want to engage with detailed trainer AI tuning though, you can just use these two composite flags, and trust that expansion will keep their contents updated to always represent the most standard and the smartest behaviour we can.

## `AI_FLAG_CHECK_BAD_MOVE`
The AI will avoid using moves that are likely to fail in the current situation. This flag helps prevent the AI from making ineffective choices, such as using moves into immunities, into invulnerable states, or when the moves are otherwise hindered by abilities, terrain, or status conditions.

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion include/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,10 @@ struct BattleHealthboxInfo
u8 animationState;
u8 partyStatusDelayTimer;
u8 matrixNum;
u8 shadowSpriteId;

u8 shadowSpriteIdPrimary;
u8 shadowSpriteIdSecondary;

u8 soundTimer;
u8 introEndDelay;
u8 field_A;
Expand Down
1 change: 0 additions & 1 deletion include/battle_ai_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ bool32 CanKnockOffItem(u32 battler, u32 item);
bool32 IsAbilityOfRating(u32 ability, s8 rating);
bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability);
bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u32 move);
u32 AI_GetBattlerMoveTargetType(u32 battlerId, u32 move);
bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove);

// stat stage checks
Expand Down
3 changes: 3 additions & 0 deletions include/battle_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ enum

#define TAG_HEALTHBAR_PAL TAG_HEALTHBAR_PLAYER1_TILE
#define TAG_HEALTHBOX_PAL TAG_HEALTHBOX_PLAYER1_TILE
#define TAG_SHADOW_PAL TAG_HEALTHBOX_PLAYER1_TILE

#define TAG_SHADOW_TILE 0xD759

#define TAG_GIMMICK_TRIGGER_TILE 0xD777
#define TAG_MEGA_INDICATOR_TILE 0xD778
Expand Down
3 changes: 3 additions & 0 deletions include/config/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,4 +279,7 @@
#define SHOW_TYPES_CAUGHT 2
#define B_SHOW_TYPES SHOW_TYPES_NEVER // When defined as SHOW_TYPES_ALWAYS, after selecting "Fight" in battle, the types of all Pokemon are revealed. Whe defined as SHOW_TYPES_OWN, types are only revealed if the player owns the mon in question.

// Pokémon battle sprite settings
#define B_ENEMY_MON_SHADOW_STYLE GEN_LATEST // In Gen4+, all enemy Pokemon will have a shadow drawn beneath them.

#endif // GUARD_CONFIG_BATTLE_H
10 changes: 6 additions & 4 deletions include/constants/event_objects.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,12 @@
#define OW_SPECIES(x) (((x)->graphicsId & OBJ_EVENT_GFX_SPECIES_MASK) - OBJ_EVENT_GFX_MON_BASE)
#define OW_FORM(x) ((x)->graphicsId >> OBJ_EVENT_GFX_SPECIES_BITS)

#define SHADOW_SIZE_S 0
#define SHADOW_SIZE_M 1
#define SHADOW_SIZE_L 2
#define SHADOW_SIZE_NONE 3 // Originally SHADOW_SIZE_XL, which went unused due to shadowSize in ObjectEventGraphicsInfo being only 2 bits.
#define SHADOW_SIZE_S 0
#define SHADOW_SIZE_M 1
#define SHADOW_SIZE_L 2
#define SHADOW_SIZE_NONE 3 // Originally SHADOW_SIZE_XL, which went unused due to shadowSize in ObjectEventGraphicsInfo being only 2 bits.

#define SHADOW_SIZE_XL_BATTLE_ONLY SHADOW_SIZE_NONE // Battle-only definition for XL shadow size.

#define F_INANIMATE (1 << 6)
#define F_DISABLE_REFLECTION_PALETTE_LOAD (1 << 7)
Expand Down
2 changes: 0 additions & 2 deletions include/field_weather.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ struct Weather
struct Sprite *sandstormSprites2[NUM_SWIRL_SANDSTORM_SPRITES];
} s2;
} sprites;
u8 darkenedContrastColorMaps[NUM_WEATHER_COLOR_MAPS][32];
u8 contrastColorMaps[NUM_WEATHER_COLOR_MAPS][32];
s8 colorMapIndex;
s8 targetColorMapIndex;
u8 colorMapStepDelay;
Expand Down
1 change: 1 addition & 0 deletions include/graphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -3104,6 +3104,7 @@ extern const u32 gBattleAnimBgPalette_Surf[];
extern const u32 gBattleAnimBackgroundImageMuddyWater_Pal[];

extern const u32 gEnemyMonShadow_Gfx[];
extern const u32 gEnemyMonShadowsSized_Gfx[];

extern const u32 gBattleAnimFogTilemap[];

Expand Down
6 changes: 6 additions & 0 deletions include/pokemon.h
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,12 @@ struct SpeciesInfo /*0xC4*/
u32 tmIlliterate:1; // This species will be unable to learn the universal moves.
u32 isFrontierBanned:1; // This species is not allowed to participate in Battle Frontier facilities.
u32 padding4:11;
// Shadow settings
s8 enemyShadowXOffset; // This determines the X-offset for an enemy Pokémon's shadow during battle; negative values point left, positive values point right.
s8 enemyShadowYOffset; // This determines the Y-offset for an enemy Pokémon's shadow during battle; negative values point up, positive values point down.
u16 enemyShadowSize:3; // This determines the size of the shadow sprite used for an enemy Pokémon's front sprite during battle.
u16 suppressEnemyShadow:1; // If set to true, then a shadow will not be drawn beneath an enemy Pokémon's front sprite during battle.
u16 padding5:12;
// Move Data
/* 0x80 */ const struct LevelUpMove *levelUpLearnset;
/* 0x84 */ const u16 *teachableLearnset;
Expand Down
20 changes: 19 additions & 1 deletion include/pokemon_sprite_visualizer.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef GUARD_POKEMON_SPRITE_VISUALIZER_H
#define GUARD_POKEMON_SPRITE_VISUALIZER_H

#include "constants/global.h"
#include "constants/pokemon_sprite_visualizer.h"

//Structs
Expand Down Expand Up @@ -43,6 +44,17 @@ struct PokemonSpriteOffsets
s8 offset_front_elevation;
};

struct PokemonShadowSettings
{
s8 definedX;
s8 definedY;
u8 definedSize;

s8 overrideX;
s8 overrideY;
u8 overrideSize;
};

struct PokemonSpriteVisualizer
{
u16 currentmonId;
Expand All @@ -52,14 +64,20 @@ struct PokemonSpriteVisualizer
u8 backspriteId;
u8 iconspriteId;
u8 followerspriteId;
u8 frontShadowSpriteId;

bool8 isShiny;
bool8 isFemale;

u8 frontShadowSpriteIdPrimary;
u8 frontShadowSpriteIdSecondary;
struct PokemonShadowSettings shadowSettings;

struct PokemonSpriteVisualizerModifyArrows modifyArrows;
struct PokemonSpriteVisualizerOptionArrows optionArrows;
struct PokemonSpriteVisualizerYPosModifiyArrows yPosModifyArrows;
struct PokemonSpriteConstValues constSpriteValues;
struct PokemonSpriteOffsets offsetsSpriteValues;

u8 animIdBack;
u8 animIdFront;
u8 battleBgType;
Expand Down
18 changes: 9 additions & 9 deletions src/battle_ai_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
s8 atkPriority = GetMovePriority(battlerAtk, move);
u32 moveEffect = gMovesInfo[move].effect;
s32 moveType;
u32 moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move);
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move);
struct AiLogicData *aiData = AI_DATA;
u32 effectiveness = aiData->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
Expand Down Expand Up @@ -2569,7 +2569,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
else
{
if (AI_GetBattlerMoveTargetType(battlerDef, instructedMove) & (MOVE_TARGET_SELECTED
if (GetBattlerMoveTargetType(battlerDef, instructedMove) & (MOVE_TARGET_SELECTED
| MOVE_TARGET_DEPENDS
| MOVE_TARGET_RANDOM
| MOVE_TARGET_BOTH
Expand Down Expand Up @@ -2767,7 +2767,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
// move data
u32 moveType = gMovesInfo[move].type;
u32 effect = gMovesInfo[move].effect;
u32 moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move);
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move);
// ally data
u32 battlerAtkPartner = BATTLE_PARTNER(battlerAtk);
struct AiLogicData *aiData = AI_DATA;
Expand Down Expand Up @@ -3098,7 +3098,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)

if (instructedMove != MOVE_NONE
&& !IS_MOVE_STATUS(instructedMove)
&& (AI_GetBattlerMoveTargetType(battlerAtkPartner, instructedMove) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) // Use instruct on multi-target moves
&& (GetBattlerMoveTargetType(battlerAtkPartner, instructedMove) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) // Use instruct on multi-target moves
{
RETURN_SCORE_PLUS(WEAK_EFFECT);
}
Expand Down Expand Up @@ -3743,24 +3743,24 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
break;
case MOVE_WIDE_GUARD:
if (predictedMove != MOVE_NONE && AI_GetBattlerMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH))
if (predictedMove != MOVE_NONE && GetBattlerMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH))
{
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
}
else if (isDoubleBattle && AI_GetBattlerMoveTargetType(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) & MOVE_TARGET_FOES_AND_ALLY)
else if (isDoubleBattle && GetBattlerMoveTargetType(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) & MOVE_TARGET_FOES_AND_ALLY)
{
if (aiData->abilities[battlerAtk] != ABILITY_TELEPATHY)
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
}
break;
case MOVE_CRAFTY_SHIELD:
if (predictedMove != MOVE_NONE && IS_MOVE_STATUS(predictedMove) && !(AI_GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
if (predictedMove != MOVE_NONE && IS_MOVE_STATUS(predictedMove) && !(GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
break;

case MOVE_MAT_BLOCK:
if (gDisableStructs[battlerAtk].isFirstTurn && predictedMove != MOVE_NONE
&& !IS_MOVE_STATUS(predictedMove) && !(AI_GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
&& !IS_MOVE_STATUS(predictedMove) && !(GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
break;
case MOVE_KINGS_SHIELD:
Expand Down Expand Up @@ -4140,7 +4140,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_MAGIC_COAT:
if (IS_MOVE_STATUS(predictedMove) && AI_GetBattlerMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_SELECTED | MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_BOTH))
if (IS_MOVE_STATUS(predictedMove) && GetBattlerMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_SELECTED | MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_BOTH))
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_RECYCLE:
Expand Down
32 changes: 15 additions & 17 deletions src/battle_ai_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1405,14 +1405,6 @@ u32 AI_GetWeather(struct AiLogicData *aiData)
return gBattleWeather;
}

u32 AI_GetBattlerMoveTargetType(u32 battlerId, u32 move)
{
if (gMovesInfo[move].effect == EFFECT_EXPANDING_FORCE && AI_IsTerrainAffected(battlerId, STATUS_FIELD_PSYCHIC_TERRAIN))
return MOVE_TARGET_BOTH;
else
return gMovesInfo[move].target;
}

bool32 IsAromaVeilProtectedMove(u32 move)
{
switch (move)
Expand Down Expand Up @@ -2113,7 +2105,7 @@ bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool
if (ignoreStatus && IS_MOVE_STATUS(moves[i]))
continue;
else if ((!IS_MOVE_STATUS(moves[i]) && gMovesInfo[moves[i]].accuracy == 0)
|| AI_GetBattlerMoveTargetType(battlerAtk, moves[i]) & (MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD))
|| GetBattlerMoveTargetType(battlerAtk, moves[i]) & (MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD))
continue;

if (AI_DATA->moveAccuracy[battlerAtk][battlerDef][i] <= accCheck)
Expand Down Expand Up @@ -3752,11 +3744,14 @@ void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)

if (AI_CanBurn(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove))
{
ADJUST_SCORE_PTR(WEAK_EFFECT); // burning is good
if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL))
if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL)
|| (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_OMNISCIENT) // Not Omniscient but expects physical attacker
&& gSpeciesInfo[gBattleMons[battlerDef].species].baseAttack >= gSpeciesInfo[gBattleMons[battlerDef].species].baseSpAttack + 10))
{
if (CanTargetFaintAi(battlerDef, battlerAtk))
ADJUST_SCORE_PTR(DECENT_EFFECT); // burning the target to stay alive is cool
if (gMovesInfo[GetBestDmgMoveFromBattler(battlerDef, battlerAtk)].category == DAMAGE_CATEGORY_PHYSICAL)
ADJUST_SCORE_PTR(DECENT_EFFECT);
else
ADJUST_SCORE_PTR(WEAK_EFFECT);
}

if (HasMoveEffectANDArg(battlerAtk, EFFECT_DOUBLE_POWER_ON_ARG_STATUS, STATUS1_BURN)
Expand Down Expand Up @@ -3833,11 +3828,14 @@ void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score

if (AI_CanGiveFrostbite(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove))
{
ADJUST_SCORE_PTR(WEAK_EFFECT); // frostbite is good
if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL))
if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL)
|| (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_OMNISCIENT) // Not Omniscient but expects special attacker
&& gSpeciesInfo[gBattleMons[battlerDef].species].baseSpAttack >= gSpeciesInfo[gBattleMons[battlerDef].species].baseAttack + 10))
{
if (CanTargetFaintAi(battlerDef, battlerAtk))
ADJUST_SCORE_PTR(DECENT_EFFECT); // frostbiting the target to stay alive is cool
if (gMovesInfo[GetBestDmgMoveFromBattler(battlerDef, battlerAtk)].category == DAMAGE_CATEGORY_SPECIAL)
ADJUST_SCORE_PTR(DECENT_EFFECT);
else
ADJUST_SCORE_PTR(WEAK_EFFECT);
}

if (HasMoveEffectANDArg(battlerAtk, EFFECT_DOUBLE_POWER_ON_ARG_STATUS, STATUS1_FROSTBITE)
Expand Down
27 changes: 22 additions & 5 deletions src/battle_anim_effects_1.c
Original file line number Diff line number Diff line change
Expand Up @@ -6616,12 +6616,29 @@ static void ReloadBattlerSprites(u32 battler, struct Pokemon *party)
UpdateIndicatorVisibilityAndType(gHealthboxSpriteIds[battler], TRUE);

// Try to recreate shadow sprite
if (gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId < MAX_SPRITES)
if (B_ENEMY_MON_SHADOW_STYLE >= GEN_4)
{
DestroySprite(&gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId]);
gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId = MAX_SPRITES;
CreateEnemyShadowSprite(battler);
SetBattlerShadowSpriteCallback(battler, GetMonData(mon, MON_DATA_SPECIES));
// Both of these *should* be true, but use an OR just to be certain
if (gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdPrimary < MAX_SPRITES
|| gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdSecondary < MAX_SPRITES)
{
DestroySprite(&gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdPrimary]);
DestroySprite(&gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdSecondary]);
gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdPrimary = MAX_SPRITES;
gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdSecondary = MAX_SPRITES;
CreateEnemyShadowSprite(battler);
SetBattlerShadowSpriteCallback(battler, GetMonData(mon, MON_DATA_SPECIES));
}
}
else
{
if (gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdPrimary < MAX_SPRITES)
{
DestroySprite(&gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdPrimary]);
gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdPrimary = MAX_SPRITES;
CreateEnemyShadowSprite(battler);
SetBattlerShadowSpriteCallback(battler, GetMonData(mon, MON_DATA_SPECIES));
}
}
}

Expand Down
36 changes: 0 additions & 36 deletions src/battle_anim_smokescreen.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@

#define TAG_SMOKESCREEN 55019

#define PALTAG_SHADOW 55039
#define GFXTAG_SHADOW 55129

static void SpriteCB_SmokescreenImpactMain(struct Sprite *);
static void SpriteCB_SmokescreenImpact(struct Sprite *);

Expand Down Expand Up @@ -95,39 +92,6 @@ static const struct SpriteTemplate sSmokescreenImpactSpriteTemplate =
.callback = SpriteCB_SmokescreenImpact
};

const struct CompressedSpriteSheet gSpriteSheet_EnemyShadow =
{
.data = gEnemyMonShadow_Gfx, .size = 0x80, .tag = GFXTAG_SHADOW
};

static const struct OamData sOamData_EnemyShadow =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.mosaic = FALSE,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(32x8),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(32x8),
.tileNum = 0,
.priority = 3,
.paletteNum = 0,
.affineParam = 0
};

const struct SpriteTemplate gSpriteTemplate_EnemyShadow =
{
.tileTag = GFXTAG_SHADOW,
.paletteTag = PALTAG_SHADOW,
.oam = &sOamData_EnemyShadow,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_SetInvisible
};

#define sActiveSprites data[0]
#define sPersist data[1]

Expand Down
Loading

0 comments on commit f48833e

Please sign in to comment.