This commit is contained in:
Kildemal 2026-03-21 23:38:09 +01:00 committed by GitHub
commit 00f1ea26ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 148 additions and 121 deletions

View File

@ -605,15 +605,6 @@ struct NatureInfo
const u8 *natureGirlMessage;
};
#define SPINDA_SPOT_WIDTH 16
#define SPINDA_SPOT_HEIGHT 16
struct SpindaSpot
{
u8 x, y;
u16 image[SPINDA_SPOT_HEIGHT];
};
struct LevelUpMove
{
enum Move move;
@ -853,7 +844,6 @@ enum HoennDexOrder SpeciesToHoennPokedexNum(enum Species species);
enum NationalDexOrder RegionalToNationalOrder(u32 regionNum);
enum NationalDexOrder KantoToNationalOrder(enum KantoDexOrder kantoNum);
enum NationalDexOrder HoennToNationalOrder(enum HoennDexOrder hoennNum);
void DrawSpindaSpots(u32 personality, u8 *dest, bool32 isSecondFrame);
void EvolutionRenameMon(struct Pokemon *mon, enum Species oldSpecies, enum Species newSpecies);
u8 GetPlayerFlankId(void);
u16 GetLinkTrainerFlankId(u8 linkPlayerId);

31
include/pokemon_spots.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef GUARD_POKEMON_SPOTS_H
#define GUARD_POKEMON_SPOTS_H
enum SpotAnimFrame {
FRAME_1,
FRAME_2
};
struct MonSpot {
u8 x, y;
const u32* image;
};
struct MonSpotTemplate {
const struct MonSpot* spots;
u8 count;
u8 scale;
s8 xOffsetFrame2;
s8 yOffsetFrame2;
u8 firstColor;
u8 lastColor;
u8 colorAdjust;
};
extern const struct MonSpotTemplate* const gSpottedSpecies[];
bool32 ShouldDrawSpotsOnSpecies(enum Species species);
void DrawPokemonSpots(u32 personality, const struct MonSpotTemplate* spotTemplate, u8 *dest, enum SpotAnimFrame SpotAnimFrame);
void DrawPokemonSpotsBothFrames(u32 personality, const struct MonSpotTemplate* spotTemplate, u8* dest);
#endif /* end of include guard: GUARD_POKEMON_SPOTS_H */

View File

@ -4,6 +4,7 @@
#include "decompress.h"
#include "decompress_error_handler.h"
#include "pokemon.h"
#include "pokemon_spots.h"
#include "pokemon_sprite_visualizer.h"
#include "text.h"
#include "menu.h"
@ -1172,10 +1173,9 @@ void LoadSpecialPokePicIsEgg(void *dest, enum Species species, u32 personality,
DecompressDataWithHeaderWram(gSpeciesInfo[SPECIES_NONE].backPic, dest);
}
if (species == SPECIES_SPINDA && isFrontPic)
if (ShouldDrawSpotsOnSpecies(species) && isFrontPic)
{
DrawSpindaSpots(personality, dest, FALSE);
DrawSpindaSpots(personality, dest, TRUE);
DrawPokemonSpotsBothFrames(personality, gSpottedSpecies[species], dest);
}
}

View File

@ -631,14 +631,6 @@ static const enum NationalDexOrder sHoennToNationalOrder[HOENN_DEX_COUNT - 1] =
HOENN_TO_NATIONAL(DEOXYS),
};
const struct SpindaSpot gSpindaSpotGraphics[] =
{
{.x = 16, .y = 7, .image = INCBIN_U16("graphics/pokemon/spinda/spots/spot_0.1bpp")},
{.x = 40, .y = 8, .image = INCBIN_U16("graphics/pokemon/spinda/spots/spot_1.1bpp")},
{.x = 22, .y = 25, .image = INCBIN_U16("graphics/pokemon/spinda/spots/spot_2.1bpp")},
{.x = 34, .y = 26, .image = INCBIN_U16("graphics/pokemon/spinda/spots/spot_3.1bpp")}
};
// In Battle Palace, moves are chosen based on the pokemons nature rather than by the player
// Moves are grouped into "Attack", "Defense", or "Support" (see PALACE_MOVE_GROUP_*)
// Each nature has a certain percent chance of selecting a move from a particular group
@ -5293,106 +5285,6 @@ enum NationalDexOrder HoennToNationalOrder(enum HoennDexOrder hoennNum)
return sHoennToNationalOrder[hoennNum - 1];
}
// Spots can be drawn on Spinda's color indexes 1, 2, or 3
#define FIRST_SPOT_COLOR 1
#define LAST_SPOT_COLOR 3
// To draw a spot pixel, add 4 to the color index
#define SPOT_COLOR_ADJUSTMENT 4
/*
The function below handles drawing the randomly-placed spots on Spinda's front sprite.
Spinda has 4 spots, each with an entry in gSpindaSpotGraphics. Each entry contains
a base x and y coordinate for the spot and a 16x16 binary image. Each bit in the image
determines whether that pixel should be considered part of the spot.
The position of each spot is randomized using the Spinda's personality. The entire 32 bit
personality value is used, 4 bits for each coordinate of the 4 spots. If the personality
value is 0x87654321, then 0x1 will be used for the 1st spot's x coord, 0x2 will be used for
the 1st spot's y coord, 0x3 will be used for the 2nd spot's x coord, and so on. Each
coordinate is calculated as (baseCoord + (given 4 bits of personality) - 8). In effect this
means each spot can start at any position -8 to +7 off of its base coordinates (256 possibilities).
The function then loops over the 16x16 spot image. For each bit in the spot's binary image, if
the bit is set then it's part of the spot; try to draw it. A pixel is drawn on Spinda if the
pixel is between FIRST_SPOT_COLOR and LAST_SPOT_COLOR (so only colors 1, 2, or 3 on Spinda will
allow a spot to be drawn). These color indexes are Spinda's light brown body colors. To create
the spot it adds 4 to the color index, so Spinda's spots will be colors 5, 6, and 7.
The above is done in TRY_DRAW_SPOT_PIXEL two different ways: one with << 4, and one without.
This is because Spinda's sprite is a 4 bits per pixel image, but the pointer to Spinda's pixels
(destPixels) is an 8 bit pointer, so it addresses two pixels. Shifting by 4 accesses the 2nd
of these pixels, so this is done every other time.
*/
// Draw spot pixel if this is Spinda's body color
#define TRY_DRAW_SPOT_PIXEL(pixels, shift) \
if (((*(pixels) & (0xF << (shift))) >= (FIRST_SPOT_COLOR << (shift))) \
&& ((*(pixels) & (0xF << (shift))) <= (LAST_SPOT_COLOR << (shift)))) \
{ \
*(pixels) += (SPOT_COLOR_ADJUSTMENT << (shift)); \
}
void DrawSpindaSpots(u32 personality, u8 *dest, bool32 isSecondFrame)
{
s32 i;
for (i = 0; i < (s32)ARRAY_COUNT(gSpindaSpotGraphics); i++)
{
s32 row;
u8 x = gSpindaSpotGraphics[i].x + (personality & 0x0F);
u8 y = gSpindaSpotGraphics[i].y + ((personality & 0xF0) >> 4);
if (isSecondFrame)
{
x -= 12;
y += 56;
}
else
{
x -= 8;
y -= 8;
}
for (row = 0; row < SPINDA_SPOT_HEIGHT; row++)
{
s32 column;
s32 spotPixelRow = gSpindaSpotGraphics[i].image[row];
for (column = x; column < x + SPINDA_SPOT_WIDTH; column++)
{
/* Get target pixels on Spinda's sprite */
u8 *destPixels = dest + ((column / 8) * TILE_SIZE_4BPP) +
((column % 8) / 2) +
((y / 8) * TILE_SIZE_4BPP * 8) +
((y % 8) * 4);
/* Is this pixel in the 16x16 spot image part of the spot? */
if (spotPixelRow & 1)
{
/* destPixels addressess two pixels, alternate which */
/* of the two pixels is being considered for drawing */
if (column & 1)
{
/* Draw spot pixel if this is Spinda's body color */
TRY_DRAW_SPOT_PIXEL(destPixels, 4);
}
else
{
/* Draw spot pixel if this is Spinda's body color */
TRY_DRAW_SPOT_PIXEL(destPixels, 0);
}
}
spotPixelRow >>= 1;
}
y++;
}
personality >>= 8;
}
}
void EvolutionRenameMon(struct Pokemon *mon, enum Species oldSpecies, enum Species newSpecies)
{
u8 language;

114
src/pokemon_spots.c Normal file
View File

@ -0,0 +1,114 @@
#include "global.h"
#include "constants/species.h"
#include "pokemon.h"
#include "pokemon_spots.h"
#define EVEN_PIXEL_SHIFT 0
#define ODD_PIXEL_SHIFT 4
#define FRAME_SIZE 64
#define MON_SPOT(name, idx, _x, _y) \
{.x = _x, .y =_y, .image = s##name##SpotImages[idx]},
/* Define Spots Below */
// SPECIES_SPINDA
static const u32 sSpindaSpotImages[4][8] = {
INCBIN_U32("graphics/pokemon/spinda/spots/spot_0.1bpp"),
INCBIN_U32("graphics/pokemon/spinda/spots/spot_1.1bpp"),
INCBIN_U32("graphics/pokemon/spinda/spots/spot_2.1bpp"),
INCBIN_U32("graphics/pokemon/spinda/spots/spot_3.1bpp"),
};
static const struct MonSpot sSpindaSpots[] = {
MON_SPOT(Spinda, 0, 16, 7)
MON_SPOT(Spinda, 1, 40, 8)
MON_SPOT(Spinda, 2, 22, 27)
MON_SPOT(Spinda, 3, 34, 26)
};
const struct MonSpotTemplate gSpindaSpotTemplate = {
.spots = sSpindaSpots,
.count = ARRAY_COUNT(sSpindaSpots),
.scale = 1,
.xOffsetFrame2 = -4,
.yOffsetFrame2 = 0,
.firstColor = 1,
.lastColor = 3,
.colorAdjust = 4,
};
/* End of Spot Defintions */
const struct MonSpotTemplate* const gSpottedSpecies[NUM_SPECIES] = {[SPECIES_SPINDA] = &gSpindaSpotTemplate};
static inline void TryDrawSpotPixel(u8* pixels, u8 pixelShift, u8 firstColor, u8 lastColor, u8 spotColorAdjust)
{
u8 pixel = (*pixels >> pixelShift) & 0xF;
if ((u8)(pixel - firstColor) <= (lastColor - firstColor))
*pixels += (spotColorAdjust << pixelShift);
}
bool32 ShouldDrawSpotsOnSpecies(enum Species species)
{
return gSpottedSpecies[SanitizeSpeciesId(species)] != NULL;
}
static inline u32 GetSpotRow(const u32* image, u32 row, u32 size)
{
u32 numRows = 32 / size;
u32 packedRow = row / numRows;
u32 idx = row & (numRows - 1);
return (image[packedRow] >> (idx * size)) & ((size == 32) ? 0xFFFFFFFFu : ((1u << size) - 1));
}
void DrawPokemonSpots(u32 personality, const struct MonSpotTemplate* spotTemplate, u8* dest,
enum SpotAnimFrame spotAnimFrame)
{
u8 size = 16 * spotTemplate->scale; // We only expect square spots
u32 i;
for (i = 0; i < spotTemplate->count; i++) {
const struct MonSpot* spot = &spotTemplate->spots[i];
u16 x = spot->x + (personality & 0x0F) * spotTemplate->scale;
u8 y = spot->y + ((personality & 0xF0) >> 4) * spotTemplate->scale;
switch (spotAnimFrame) {
case FRAME_1:
x -= size / 2;
y -= size / 2;
break;
case FRAME_2:
x += spotTemplate->xOffsetFrame2 - size / 2;
y += FRAME_SIZE + spotTemplate->yOffsetFrame2 - size / 2;
break;
default:
break;
}
for (u32 row = 0; row < size; row++) {
u32 spotPixelRow = GetSpotRow(spot->image, row, size);
for (u32 column = x; column < x + size; column++) {
u8* destPixels = dest + ((column / 8) * TILE_SIZE_4BPP) + ((column % 8) / 2) +
((y / 8) * TILE_SIZE_4BPP * 8) + ((y % 8) * 4);
if (spotPixelRow & 1) {
TryDrawSpotPixel(destPixels, (column & 1 ? 4 : 0), spotTemplate->firstColor,
spotTemplate->lastColor, spotTemplate->colorAdjust);
}
spotPixelRow >>= 1;
}
y++;
}
personality >>= 8;
}
}
void DrawPokemonSpotsBothFrames(u32 personality, const struct MonSpotTemplate* spotTemplate, u8* dest)
{
DrawPokemonSpots(personality, spotTemplate, dest, FALSE);
DrawPokemonSpots(personality, spotTemplate, dest, TRUE);
}