pokeemerald/src/pokedex_cry_screen.c

579 lines
20 KiB
C
Raw Normal View History

2018-11-28 17:14:32 +01:00
#include "global.h"
#include "bg.h"
#include "m4a.h"
#include "main.h"
#include "malloc.h"
2018-11-28 17:14:32 +01:00
#include "palette.h"
#include "pokedex_cry_screen.h"
#include "sound.h"
#include "trig.h"
#include "window.h"
2020-03-10 19:02:03 +01:00
// Cry meter needle positions
//
// 0
// 32 . . -32
// . .
// 64 . . -64
// . .
// . .
// 96 . . -96
// 127
//
2020-03-10 19:02:03 +01:00
#define MIN_NEEDLE_POS 32
#define MAX_NEEDLE_POS -32
#define NEEDLE_MOVE_INCREMENT 5
#define WAVEFORM_WINDOW_HEIGHT 56
struct PokedexCryMeterNeedle {
s8 rotation;
s8 targetRotation;
u8 moveIncrement;
u16 spriteId;
2018-11-28 17:14:32 +01:00
};
struct PokedexCryScreen
{
2020-03-10 19:02:03 +01:00
u8 cryWaveformBuffer[16];
u8 cryState;
u8 playhead;
u8 waveformPreviousY;
u16 unk; // Never read
u8 playStartPos;
2018-11-28 17:14:32 +01:00
u16 species;
2020-03-10 19:02:03 +01:00
u8 cryOverrideCountdown;
u8 cryRepeatDelay;
2018-11-28 17:14:32 +01:00
};
2020-03-08 18:24:22 +01:00
static void PlayCryScreenCry(u16);
2020-03-10 19:02:03 +01:00
static void BufferCryWaveformSegment(void);
2020-03-08 18:24:22 +01:00
static void DrawWaveformFlatline(void);
2020-03-10 19:02:03 +01:00
static void AdvancePlayhead(u8);
2020-03-08 18:24:22 +01:00
static void DrawWaveformSegment(u8, u8);
2020-03-10 19:02:03 +01:00
static void DrawWaveformWindow(u8);
static void ShiftWaveformOver(u8, s16, bool8);
2020-03-08 18:24:22 +01:00
static void SpriteCB_CryMeterNeedle(struct Sprite *);
2020-03-10 19:02:03 +01:00
static void SetCryMeterNeedleTarget(s8);
2018-11-28 17:14:32 +01:00
2019-02-02 12:44:00 +01:00
// IWRAM common
u8 gDexCryScreenState;
// EWRAM vars
2018-11-28 17:14:32 +01:00
static EWRAM_DATA struct PokedexCryScreen *sDexCryScreen = NULL;
static EWRAM_DATA u8 *sCryWaveformWindowTiledata = NULL;
2020-03-10 19:02:03 +01:00
static EWRAM_DATA struct PokedexCryMeterNeedle *sCryMeterNeedle = NULL;
2018-11-28 17:14:32 +01:00
2020-03-08 18:24:22 +01:00
static const u16 sCryMeterNeedle_Pal[] = INCBIN_U16("graphics/pokedex/cry_meter_needle.gbapal");
static const u8 sCryMeterNeedle_Gfx[] = INCBIN_U8("graphics/pokedex/cry_meter_needle.4bpp");
2018-11-28 17:14:32 +01:00
2020-03-08 18:24:22 +01:00
static const u16 sCryMeter_Tilemap[] = INCBIN_U16("graphics/pokedex/cry_meter_map.bin"); // Unused
static const u16 sCryMeter_Pal[] = INCBIN_U16("graphics/pokedex/cry_meter.gbapal");
static const u8 sCryMeter_Gfx[] = INCBIN_U8("graphics/pokedex/cry_meter.4bpp.lz");
2018-11-28 17:14:32 +01:00
2020-03-10 19:02:03 +01:00
static const u16 sWaveformOffsets[][72] =
{
2018-11-28 17:14:32 +01:00
{
0x0000, 0x0004, 0x0008, 0x000C, 0x0010, 0x0014, 0x0018, 0x001C,
0x0400, 0x0404, 0x0408, 0x040C, 0x0410, 0x0414, 0x0418, 0x041C,
0x0800, 0x0804, 0x0808, 0x080C, 0x0810, 0x0814, 0x0818, 0x081C,
0x0C00, 0x0C04, 0x0C08, 0x0C0C, 0x0C10, 0x0C14, 0x0C18, 0x0C1C,
0x1000, 0x1004, 0x1008, 0x100C, 0x1010, 0x1014, 0x1018, 0x101C,
0x1400, 0x1404, 0x1408, 0x140C, 0x1410, 0x1414, 0x1418, 0x141C,
0x1800, 0x1804, 0x1808, 0x180C, 0x1810, 0x1814, 0x1818, 0x181C,
0x1C00, 0x1C04, 0x1C08, 0x1C0C, 0x1C10, 0x1C14, 0x1C18, 0x1C1C,
0x2000, 0x2004, 0x2008, 0x200C, 0x2010, 0x2014, 0x2018, 0x201C
}, {
0x0000, 0x0004, 0x0008, 0x000C, 0x0010, 0x0014, 0x0018, 0x001C,
0x0400, 0x0404, 0x0408, 0x040C, 0x0410, 0x0414, 0x0418, 0x041C,
0x0800, 0x0804, 0x0808, 0x080C, 0x0810, 0x0814, 0x0818, 0x081C,
0x0C00, 0x0C04, 0x0C08, 0x0C0C, 0x0C10, 0x0C14, 0x0C18, 0x0C1C,
0x1000, 0x1004, 0x1008, 0x100C, 0x1010, 0x1014, 0x1018, 0x101C,
0x1400, 0x1404, 0x1408, 0x140C, 0x1410, 0x1414, 0x1418, 0x141C,
0x1800, 0x1804, 0x1808, 0x180C, 0x1810, 0x1814, 0x1818, 0x181C,
0x1C00, 0x1C04, 0x1C08, 0x1C0C, 0x1C10, 0x1C14, 0x1C18, 0x1C1C,
0x2000, 0x2004, 0x2008, 0x200C, 0x2010, 0x2014, 0x2018, 0x201C
}, {
0x0001, 0x0005, 0x0009, 0x000D, 0x0011, 0x0015, 0x0019, 0x001D,
0x0401, 0x0405, 0x0409, 0x040D, 0x0411, 0x0415, 0x0419, 0x041D,
0x0801, 0x0805, 0x0809, 0x080D, 0x0811, 0x0815, 0x0819, 0x081D,
0x0C01, 0x0C05, 0x0C09, 0x0C0D, 0x0C11, 0x0C15, 0x0C19, 0x0C1D,
0x1001, 0x1005, 0x1009, 0x100D, 0x1011, 0x1015, 0x1019, 0x101D,
0x1401, 0x1405, 0x1409, 0x140D, 0x1411, 0x1415, 0x1419, 0x141D,
0x1801, 0x1805, 0x1809, 0x180D, 0x1811, 0x1815, 0x1819, 0x181D,
0x1C01, 0x1C05, 0x1C09, 0x1C0D, 0x1C11, 0x1C15, 0x1C19, 0x1C1D,
0x2001, 0x2005, 0x2009, 0x200D, 0x2011, 0x2015, 0x2019, 0x201D
}, {
0x0001, 0x0005, 0x0009, 0x000D, 0x0011, 0x0015, 0x0019, 0x001D,
0x0401, 0x0405, 0x0409, 0x040D, 0x0411, 0x0415, 0x0419, 0x041D,
0x0801, 0x0805, 0x0809, 0x080D, 0x0811, 0x0815, 0x0819, 0x081D,
0x0C01, 0x0C05, 0x0C09, 0x0C0D, 0x0C11, 0x0C15, 0x0C19, 0x0C1D,
0x1001, 0x1005, 0x1009, 0x100D, 0x1011, 0x1015, 0x1019, 0x101D,
0x1401, 0x1405, 0x1409, 0x140D, 0x1411, 0x1415, 0x1419, 0x141D,
0x1801, 0x1805, 0x1809, 0x180D, 0x1811, 0x1815, 0x1819, 0x181D,
0x1C01, 0x1C05, 0x1C09, 0x1C0D, 0x1C11, 0x1C15, 0x1C19, 0x1C1D,
0x2001, 0x2005, 0x2009, 0x200D, 0x2011, 0x2015, 0x2019, 0x201D
}, {
0x0002, 0x0006, 0x000A, 0x000E, 0x0012, 0x0016, 0x001A, 0x001E,
0x0402, 0x0406, 0x040A, 0x040E, 0x0412, 0x0416, 0x041A, 0x041E,
0x0802, 0x0806, 0x080A, 0x080E, 0x0812, 0x0816, 0x081A, 0x081E,
0x0C02, 0x0C06, 0x0C0A, 0x0C0E, 0x0C12, 0x0C16, 0x0C1A, 0x0C1E,
0x1002, 0x1006, 0x100A, 0x100E, 0x1012, 0x1016, 0x101A, 0x101E,
0x1402, 0x1406, 0x140A, 0x140E, 0x1412, 0x1416, 0x141A, 0x141E,
0x1802, 0x1806, 0x180A, 0x180E, 0x1812, 0x1816, 0x181A, 0x181E,
0x1C02, 0x1C06, 0x1C0A, 0x1C0E, 0x1C12, 0x1C16, 0x1C1A, 0x1C1E,
0x2002, 0x2006, 0x200A, 0x200E, 0x2012, 0x2016, 0x201A, 0x201E
}, {
0x0002, 0x0006, 0x000A, 0x000E, 0x0012, 0x0016, 0x001A, 0x001E,
0x0402, 0x0406, 0x040A, 0x040E, 0x0412, 0x0416, 0x041A, 0x041E,
0x0802, 0x0806, 0x080A, 0x080E, 0x0812, 0x0816, 0x081A, 0x081E,
0x0C02, 0x0C06, 0x0C0A, 0x0C0E, 0x0C12, 0x0C16, 0x0C1A, 0x0C1E,
0x1002, 0x1006, 0x100A, 0x100E, 0x1012, 0x1016, 0x101A, 0x101E,
0x1402, 0x1406, 0x140A, 0x140E, 0x1412, 0x1416, 0x141A, 0x141E,
0x1802, 0x1806, 0x180A, 0x180E, 0x1812, 0x1816, 0x181A, 0x181E,
0x1C02, 0x1C06, 0x1C0A, 0x1C0E, 0x1C12, 0x1C16, 0x1C1A, 0x1C1E,
0x2002, 0x2006, 0x200A, 0x200E, 0x2012, 0x2016, 0x201A, 0x201E
}, {
0x0003, 0x0007, 0x000B, 0x000F, 0x0013, 0x0017, 0x001B, 0x001F,
0x0403, 0x0407, 0x040B, 0x040F, 0x0413, 0x0417, 0x041B, 0x041F,
0x0803, 0x0807, 0x080B, 0x080F, 0x0813, 0x0817, 0x081B, 0x081F,
0x0C03, 0x0C07, 0x0C0B, 0x0C0F, 0x0C13, 0x0C17, 0x0C1B, 0x0C1F,
0x1003, 0x1007, 0x100B, 0x100F, 0x1013, 0x1017, 0x101B, 0x101F,
0x1403, 0x1407, 0x140B, 0x140F, 0x1413, 0x1417, 0x141B, 0x141F,
0x1803, 0x1807, 0x180B, 0x180F, 0x1813, 0x1817, 0x181B, 0x181F,
0x1C03, 0x1C07, 0x1C0B, 0x1C0F, 0x1C13, 0x1C17, 0x1C1B, 0x1C1F,
0x2003, 0x2007, 0x200B, 0x200F, 0x2013, 0x2017, 0x201B, 0x201F
}, {
0x0003, 0x0007, 0x000B, 0x000F, 0x0013, 0x0017, 0x001B, 0x001F,
0x0403, 0x0407, 0x040B, 0x040F, 0x0413, 0x0417, 0x041B, 0x041F,
0x0803, 0x0807, 0x080B, 0x080F, 0x0813, 0x0817, 0x081B, 0x081F,
0x0C03, 0x0C07, 0x0C0B, 0x0C0F, 0x0C13, 0x0C17, 0x0C1B, 0x0C1F,
0x1003, 0x1007, 0x100B, 0x100F, 0x1013, 0x1017, 0x101B, 0x101F,
0x1403, 0x1407, 0x140B, 0x140F, 0x1413, 0x1417, 0x141B, 0x141F,
0x1803, 0x1807, 0x180B, 0x180F, 0x1813, 0x1817, 0x181B, 0x181F,
0x1C03, 0x1C07, 0x1C0B, 0x1C0F, 0x1C13, 0x1C17, 0x1C1B, 0x1C1F,
0x2003, 0x2007, 0x200B, 0x200F, 0x2013, 0x2017, 0x201B, 0x201F
}
};
2020-03-10 19:02:03 +01:00
static const u16 sCryScreenBg_Pal[] = INCBIN_U16("graphics/pokedex/cry_screen_bg.gbapal");
static const u8 sCryScreenBg_Gfx[] = INCBIN_U8("graphics/pokedex/cry_screen_bg.4bpp");
2018-11-28 17:14:32 +01:00
2020-03-10 19:02:03 +01:00
static const u8 sWaveformTileDataNybbleMasks[] = {0xF0, 0x0F};
// Waveform is blue in the middle (8) grading to white at peaks (15)
// Split into two arrays for the two vertical slice halves
static const u8 sWaveformColor[][16] =
{
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
15, 14, 13, 12, 11, 10, 9, 8,
8, 9, 10, 11, 12, 13, 14, 15,
2018-11-28 17:14:32 +01:00
}, {
2020-03-10 19:02:03 +01:00
15 << 4, 14 << 4, 13 << 4, 12 << 4, 11 << 4, 10 << 4, 9 << 4, 8 << 4,
8 << 4, 9 << 4, 10 << 4, 11 << 4, 12 << 4, 13 << 4, 14 << 4, 15 << 4,
2018-11-28 17:14:32 +01:00
}
};
2020-03-08 18:24:22 +01:00
static const union AnimCmd sSpriteAnim_CryMeterNeedle[] =
{
2018-11-28 17:14:32 +01:00
ANIMCMD_FRAME(0, 30),
ANIMCMD_END
};
2020-03-08 18:24:22 +01:00
static const union AnimCmd *const sSpriteAnimTable_CryMeterNeedle[] =
{
2020-03-08 18:24:22 +01:00
sSpriteAnim_CryMeterNeedle
2018-11-28 17:14:32 +01:00
};
2020-03-08 18:24:22 +01:00
static const struct OamData sOamData_CryMeterNeedle =
{
2021-04-15 08:04:01 +02:00
.y = DISPLAY_HEIGHT,
2018-11-28 17:14:32 +01:00
.affineMode = ST_OAM_AFFINE_NORMAL,
2019-04-02 00:31:10 +02:00
.objMode = ST_OAM_OBJ_NORMAL,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(64x64),
2019-04-02 00:31:10 +02:00
.x = 0,
.size = SPRITE_SIZE(64x64),
2019-04-02 00:31:10 +02:00
.tileNum = 0,
.priority = 1,
.paletteNum = 0,
2018-11-28 17:14:32 +01:00
};
2020-03-08 18:24:22 +01:00
static const struct SpriteTemplate sCryMeterNeedleSpriteTemplate =
{
2020-03-08 18:24:22 +01:00
.tileTag = 0x2000,
.paletteTag = 0x2000,
.oam = &sOamData_CryMeterNeedle,
.anims = sSpriteAnimTable_CryMeterNeedle,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_CryMeterNeedle
2018-11-28 17:14:32 +01:00
};
2020-03-08 18:24:22 +01:00
static const struct SpriteSheet sCryMeterNeedleSpriteSheets[] =
{
2020-03-08 18:24:22 +01:00
{sCryMeterNeedle_Gfx, 0x800, 0x2000},
2018-11-28 17:14:32 +01:00
{}
};
2020-03-08 18:24:22 +01:00
static const struct SpritePalette sCryMeterNeedleSpritePalettes[] =
{
2020-03-08 18:24:22 +01:00
{sCryMeterNeedle_Pal, 0x2000},
2018-11-28 17:14:32 +01:00
{}
};
2020-03-10 19:02:03 +01:00
bool8 LoadCryWaveformWindow(struct CryScreenWindow *window, u8 windowId)
2018-11-28 17:14:32 +01:00
{
u8 i;
2020-03-08 18:24:22 +01:00
u8 finished = FALSE;
2018-11-28 17:14:32 +01:00
switch (gDexCryScreenState)
{
case 0:
if (!sDexCryScreen)
{
sDexCryScreen = AllocZeroed(sizeof(*sDexCryScreen));
sCryWaveformWindowTiledata = (u8 *)GetWindowAttribute(windowId, WINDOW_TILE_DATA);
2018-11-28 17:14:32 +01:00
}
2020-03-10 19:02:03 +01:00
sDexCryScreen->unk = window->unk0;
sDexCryScreen->playStartPos = window->yPos;
sDexCryScreen->cryOverrideCountdown = 0;
sDexCryScreen->cryRepeatDelay = 0;
sDexCryScreen->cryState = 0;
sDexCryScreen->waveformPreviousY = WAVEFORM_WINDOW_HEIGHT / 2;
sDexCryScreen->playhead = 0;
ShiftWaveformOver(windowId, -8 * window->xPos, TRUE); // Does nothing
2018-11-28 17:14:32 +01:00
for (i = 0; i < 224; i++)
2020-03-08 18:24:22 +01:00
CopyToWindowPixelBuffer(windowId, sCryScreenBg_Gfx, TILE_SIZE_4BPP, i);
2018-11-28 17:14:32 +01:00
gDexCryScreenState++;
break;
case 1:
2020-03-10 19:02:03 +01:00
for (i = 0; i < sDexCryScreen->playStartPos * 8; i++)
2020-03-08 18:24:22 +01:00
DrawWaveformSegment(i, 0);
2018-11-28 17:14:32 +01:00
gDexCryScreenState++;
break;
case 2:
2020-03-10 19:02:03 +01:00
DrawWaveformWindow(windowId);
LoadPalette(sCryScreenBg_Pal, window->paletteNo * 16, 32);
2020-03-08 18:24:22 +01:00
finished = TRUE;
2018-11-28 17:14:32 +01:00
break;
}
2020-03-08 18:24:22 +01:00
return finished;
2018-11-28 17:14:32 +01:00
}
2020-03-08 18:24:22 +01:00
void UpdateCryWaveformWindow(u8 windowId)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
u8 waveformIdx;
DrawWaveformWindow(windowId);
AdvancePlayhead(windowId);
2018-11-28 17:14:32 +01:00
2020-03-10 19:02:03 +01:00
// Cry cant be replayed until this counter is done
if (sDexCryScreen->cryRepeatDelay)
sDexCryScreen->cryRepeatDelay--;
2018-11-28 17:14:32 +01:00
2020-03-10 19:02:03 +01:00
// Once a cry replay has started, it waits for this countdown before playing
if (sDexCryScreen->cryOverrideCountdown)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
sDexCryScreen->cryOverrideCountdown--;
if (!sDexCryScreen->cryOverrideCountdown)
2018-11-28 17:14:32 +01:00
{
2020-03-08 18:24:22 +01:00
PlayCryScreenCry(sDexCryScreen->species);
DrawWaveformFlatline();
2018-11-28 17:14:32 +01:00
return;
}
}
2020-03-10 19:02:03 +01:00
// No cry playing
if (sDexCryScreen->cryState == 0)
2018-11-28 17:14:32 +01:00
{
2020-03-08 18:24:22 +01:00
DrawWaveformFlatline();
2018-11-28 17:14:32 +01:00
return;
}
2020-03-10 19:02:03 +01:00
// Cry playing, buffer waveform
if (sDexCryScreen->cryState == 1)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
BufferCryWaveformSegment();
2018-11-28 17:14:32 +01:00
}
2020-03-10 19:02:03 +01:00
else if (sDexCryScreen->cryState > 8)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
// Buffered waveform exhausted, end or buffer more
2018-11-28 17:14:32 +01:00
if (!IsCryPlaying())
{
2020-03-08 18:24:22 +01:00
DrawWaveformFlatline();
2020-03-10 19:02:03 +01:00
sDexCryScreen->cryState = 0;
2018-11-28 17:14:32 +01:00
return;
}
2020-03-10 19:02:03 +01:00
BufferCryWaveformSegment();
sDexCryScreen->cryState = 1;
2018-11-28 17:14:32 +01:00
}
2020-03-10 19:02:03 +01:00
// Draw cry
waveformIdx = 2 * (sDexCryScreen->cryState - 1);
DrawWaveformSegment(sDexCryScreen->playStartPos * 8 + sDexCryScreen->playhead - 2, sDexCryScreen->cryWaveformBuffer[waveformIdx]);
DrawWaveformSegment(sDexCryScreen->playStartPos * 8 + sDexCryScreen->playhead - 1, sDexCryScreen->cryWaveformBuffer[waveformIdx + 1]);
sDexCryScreen->cryState++;
2018-11-28 17:14:32 +01:00
}
2020-03-08 18:24:22 +01:00
void CryScreenPlayButton(u16 species)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
if (gMPlayInfo_BGM.status & MUSICPLAYER_STATUS_PAUSE && !sDexCryScreen->cryOverrideCountdown)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
if (!sDexCryScreen->cryRepeatDelay)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
sDexCryScreen->cryRepeatDelay = 4;
2018-11-28 17:14:32 +01:00
if (IsCryPlaying() == TRUE)
{
StopCry();
sDexCryScreen->species = species;
2020-03-10 19:02:03 +01:00
sDexCryScreen->cryOverrideCountdown = 2;
2018-11-28 17:14:32 +01:00
}
else
{
2020-03-08 18:24:22 +01:00
PlayCryScreenCry(species);
2018-11-28 17:14:32 +01:00
}
}
}
}
2020-03-08 18:24:22 +01:00
static void PlayCryScreenCry(u16 species)
2018-11-28 17:14:32 +01:00
{
2021-11-07 19:54:44 +01:00
PlayCry_NormalNoDucking(species, 0, CRY_VOLUME_RS, CRY_PRIORITY_NORMAL);
2020-03-10 19:02:03 +01:00
sDexCryScreen->cryState = 1;
2018-11-28 17:14:32 +01:00
}
2020-03-10 19:02:03 +01:00
static void BufferCryWaveformSegment(void)
2018-11-28 17:14:32 +01:00
{
u8 i;
s8 *baseBuffer;
s8 *buffer;
if (gPcmDmaCounter < 2)
baseBuffer = gSoundInfo.pcmBuffer;
else
baseBuffer = gSoundInfo.pcmBuffer + (gSoundInfo.pcmDmaPeriod + 1 - gPcmDmaCounter) * gSoundInfo.pcmSamplesPerVBlank;
buffer = baseBuffer + 0x630;
2020-03-10 19:02:03 +01:00
for (i = 0; i < ARRAY_COUNT(sDexCryScreen->cryWaveformBuffer); i++)
sDexCryScreen->cryWaveformBuffer[i] = buffer[i * 2] * 2;
2018-11-28 17:14:32 +01:00
}
2020-03-08 18:24:22 +01:00
static void DrawWaveformFlatline(void)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
DrawWaveformSegment(sDexCryScreen->playStartPos * 8 + sDexCryScreen->playhead - 2, 0);
DrawWaveformSegment(sDexCryScreen->playStartPos * 8 + sDexCryScreen->playhead - 1, 0);
2018-11-28 17:14:32 +01:00
}
2020-03-10 19:02:03 +01:00
static void AdvancePlayhead(u8 windowId)
2018-11-28 17:14:32 +01:00
{
u8 i;
u16 offset;
2020-03-10 19:02:03 +01:00
ShiftWaveformOver(windowId, sDexCryScreen->playhead, FALSE);
sDexCryScreen->playhead += 2;
offset = (sDexCryScreen->playhead / 8 + sDexCryScreen->playStartPos + 1) % 32;
2018-11-28 17:14:32 +01:00
for (i = 0; i < 7; i++)
2020-03-08 18:24:22 +01:00
CopyToWindowPixelBuffer(windowId, sCryScreenBg_Gfx, TILE_SIZE_4BPP, offset + (i * TILE_SIZE_4BPP));
2018-11-28 17:14:32 +01:00
}
2020-03-10 19:02:03 +01:00
// Waveform segments are drawn in alternate vertical slices
// Note that the waveform isnt put on screen until DrawWaveformWindow
static void DrawWaveformSegment(u8 position, u8 amplitude)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
// Position is a bitfield containing the play start pos, the playhead pos, and which vertical slice half to draw
#define PLAY_START_POS (position >> 3)
#define PLAYHEAD_POS (position & ((1 << 3) - 1))
#define VERT_SLICE (position & 1)
2018-11-28 17:14:32 +01:00
2020-03-10 19:02:03 +01:00
u8 currentPointY;
u8 nybble;
u16 offset;
u16 temp;
u8 y;
temp = (amplitude + 127) * 256;
y = temp / 1152.0;
if (y > WAVEFORM_WINDOW_HEIGHT - 1)
y = WAVEFORM_WINDOW_HEIGHT - 1;
currentPointY = y;
nybble = VERT_SLICE;
if (y > sDexCryScreen->waveformPreviousY)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
// Current point lower than previous point, draw point and draw line up to previous
2018-11-28 17:14:32 +01:00
do
{
2020-03-10 19:02:03 +01:00
offset = sWaveformOffsets[PLAYHEAD_POS][y] + PLAY_START_POS * TILE_SIZE_4BPP;
sCryWaveformWindowTiledata[offset] &= sWaveformTileDataNybbleMasks[nybble];
sCryWaveformWindowTiledata[offset] |= sWaveformColor[nybble][((y / 3) - 1) & 0x0F];
y--;
} while (y > sDexCryScreen->waveformPreviousY);
2018-11-28 17:14:32 +01:00
}
else
{
2020-03-10 19:02:03 +01:00
// Current point higher than previous point, draw point and draw line down to previous
2018-11-28 17:14:32 +01:00
do
{
2020-03-10 19:02:03 +01:00
offset = sWaveformOffsets[PLAYHEAD_POS][y] + PLAY_START_POS * TILE_SIZE_4BPP;
sCryWaveformWindowTiledata[offset] &= sWaveformTileDataNybbleMasks[nybble];
sCryWaveformWindowTiledata[offset] |= sWaveformColor[nybble][((y / 3) - 1) & 0x0F];
y++;
} while (y < sDexCryScreen->waveformPreviousY);
2018-11-28 17:14:32 +01:00
}
2020-03-10 19:02:03 +01:00
sDexCryScreen->waveformPreviousY = currentPointY;
2018-11-28 17:14:32 +01:00
}
2020-03-10 19:02:03 +01:00
static void DrawWaveformWindow(u8 windowId)
2018-11-28 17:14:32 +01:00
{
2021-11-03 20:29:18 +01:00
CopyWindowToVram(windowId, COPYWIN_GFX);
2018-11-28 17:14:32 +01:00
}
2020-03-10 19:02:03 +01:00
// rsVertical is leftover from a very different version of this function in RS
// In RS, when TRUE it would use VOFS and when FALSE it would use HOFS (only FALSE was used)
// Here when TRUE it does nothing
static void ShiftWaveformOver(u8 windowId, s16 offset, bool8 rsVertical)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
if (!rsVertical)
2018-11-28 17:14:32 +01:00
{
u8 bg = GetWindowAttribute(windowId, WINDOW_BG);
2021-11-04 04:02:06 +01:00
ChangeBgX(bg, offset << 8, BG_COORD_SET);
2018-11-28 17:14:32 +01:00
}
}
2020-03-10 19:02:03 +01:00
bool8 LoadCryMeter(struct CryScreenWindow *window, u8 windowId)
2018-11-28 17:14:32 +01:00
{
2020-03-08 18:24:22 +01:00
bool8 finished = FALSE;
2018-11-28 17:14:32 +01:00
switch (gDexCryScreenState)
{
case 0:
2020-03-10 19:02:03 +01:00
if (!sCryMeterNeedle)
sCryMeterNeedle = AllocZeroed(sizeof(*sCryMeterNeedle));
2018-11-28 17:14:32 +01:00
2020-03-08 18:24:22 +01:00
CopyToWindowPixelBuffer(windowId, sCryMeter_Gfx, 0, 0);
2020-03-10 19:02:03 +01:00
LoadPalette(sCryMeter_Pal, window->paletteNo * 16, 32);
2018-11-28 17:14:32 +01:00
gDexCryScreenState++;
break;
case 1:
2020-03-08 18:24:22 +01:00
LoadSpriteSheets(sCryMeterNeedleSpriteSheets);
LoadSpritePalettes(sCryMeterNeedleSpritePalettes);
2020-03-10 19:02:03 +01:00
sCryMeterNeedle->spriteId = CreateSprite(&sCryMeterNeedleSpriteTemplate, 40 + window->xPos * 8, 56 + window->yPos * 8, 1);
sCryMeterNeedle->rotation = MIN_NEEDLE_POS;
sCryMeterNeedle->targetRotation = MIN_NEEDLE_POS;
sCryMeterNeedle->moveIncrement = 0;
2020-03-08 18:24:22 +01:00
finished = TRUE;
2018-11-28 17:14:32 +01:00
break;
}
2020-03-08 18:24:22 +01:00
return finished;
2018-11-28 17:14:32 +01:00
}
2020-03-08 18:24:22 +01:00
void FreeCryScreen(void)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
FreeSpritePaletteByTag(GetSpritePaletteTagByPaletteNum(gSprites[sCryMeterNeedle->spriteId].oam.paletteNum));
DestroySprite(gSprites + sCryMeterNeedle->spriteId);
2018-11-28 17:14:32 +01:00
FREE_AND_SET_NULL(sDexCryScreen);
2020-03-10 19:02:03 +01:00
FREE_AND_SET_NULL(sCryMeterNeedle);
2018-11-28 17:14:32 +01:00
}
2020-03-08 18:24:22 +01:00
static void SpriteCB_CryMeterNeedle(struct Sprite *sprite)
2018-11-28 17:14:32 +01:00
{
u16 i;
2020-03-10 19:02:03 +01:00
s8 peakAmplitude;
2018-11-28 17:14:32 +01:00
s16 x;
s16 y;
struct ObjAffineSrcData affine;
struct OamMatrix matrix;
2020-03-10 19:02:03 +01:00
u8 amplitude;
2018-11-28 17:14:32 +01:00
2020-03-10 19:02:03 +01:00
gSprites[sCryMeterNeedle->spriteId].oam.affineMode = ST_OAM_AFFINE_NORMAL;
gSprites[sCryMeterNeedle->spriteId].oam.affineParam = 0;
// While no cry is playing, cryState is 0
// While cry is playing, cryState loops 1-8
switch (sDexCryScreen->cryState)
2018-11-28 17:14:32 +01:00
{
2020-03-08 18:24:22 +01:00
case 0:
2020-03-10 19:02:03 +01:00
sCryMeterNeedle->targetRotation = MIN_NEEDLE_POS;
if (sCryMeterNeedle->rotation > 0)
2020-03-08 18:24:22 +01:00
{
2020-03-10 19:02:03 +01:00
if (sCryMeterNeedle->moveIncrement != 1)
sCryMeterNeedle->moveIncrement--;
2020-03-08 18:24:22 +01:00
}
else
2020-03-10 19:02:03 +01:00
{
sCryMeterNeedle->moveIncrement = NEEDLE_MOVE_INCREMENT;
}
2020-03-08 18:24:22 +01:00
break;
case 2:
2020-03-10 19:02:03 +01:00
peakAmplitude = 0;
for (i = 0; i < ARRAY_COUNT(sDexCryScreen->cryWaveformBuffer); i++)
2020-03-08 18:24:22 +01:00
{
2020-03-10 19:02:03 +01:00
if (peakAmplitude < sDexCryScreen->cryWaveformBuffer[i])
peakAmplitude = sDexCryScreen->cryWaveformBuffer[i];
2020-03-08 18:24:22 +01:00
}
2020-03-10 19:02:03 +01:00
SetCryMeterNeedleTarget(peakAmplitude * 208 / 256);
2020-03-08 18:24:22 +01:00
break;
case 6:
2020-03-10 19:02:03 +01:00
// To introduce some randomness, needle jumps to set pos in waveform rather than peak
amplitude = sDexCryScreen->cryWaveformBuffer[10];
SetCryMeterNeedleTarget(amplitude * 208 / 256);
2020-03-08 18:24:22 +01:00
break;
2018-11-28 17:14:32 +01:00
}
2020-03-10 19:02:03 +01:00
if (sCryMeterNeedle->rotation == sCryMeterNeedle->targetRotation)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
// Empty, needle has reached target
2018-11-28 17:14:32 +01:00
}
2020-03-10 19:02:03 +01:00
else if (sCryMeterNeedle->rotation < sCryMeterNeedle->targetRotation)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
// Rotate needle left
sCryMeterNeedle->rotation += sCryMeterNeedle->moveIncrement;
if (sCryMeterNeedle->rotation > sCryMeterNeedle->targetRotation)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
sCryMeterNeedle->rotation = sCryMeterNeedle->targetRotation;
sCryMeterNeedle->targetRotation = 0;
2018-11-28 17:14:32 +01:00
}
}
else
{
2020-03-10 19:02:03 +01:00
// Rotate needle right
sCryMeterNeedle->rotation -= sCryMeterNeedle->moveIncrement;
if (sCryMeterNeedle->rotation < sCryMeterNeedle->targetRotation)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
sCryMeterNeedle->rotation = sCryMeterNeedle->targetRotation;
sCryMeterNeedle->targetRotation = 0;
2018-11-28 17:14:32 +01:00
}
}
2020-03-10 19:02:03 +01:00
affine.xScale = 256;
affine.yScale = 256;
affine.rotation = sCryMeterNeedle->rotation * 256;
2018-11-28 17:14:32 +01:00
ObjAffineSet(&affine, &matrix, 1, 2);
SetOamMatrix(0, matrix.a, matrix.b, matrix.c, matrix.d);
2020-03-10 19:02:03 +01:00
x = gSineTable[((sCryMeterNeedle->rotation + 0x7F) & 0xFF)];
y = gSineTable[((sCryMeterNeedle->rotation + 0x7F) & 0xFF) + 64];
2021-07-07 15:11:52 +02:00
sprite->x2 = x * 24 / 256;
sprite->y2 = y * 24 / 256;
2018-11-28 17:14:32 +01:00
}
2020-03-10 19:02:03 +01:00
static void SetCryMeterNeedleTarget(s8 offset)
2018-11-28 17:14:32 +01:00
{
2020-03-10 19:02:03 +01:00
u16 rotation = (MIN_NEEDLE_POS - offset) & 0xFF;
// Min is positive, max is negative. Make sure needle hasnt moved out of bounds
if (rotation > MIN_NEEDLE_POS && rotation < (u8)MAX_NEEDLE_POS)
rotation = (u8)MAX_NEEDLE_POS;
2018-11-28 17:14:32 +01:00
2020-03-10 19:02:03 +01:00
sCryMeterNeedle->targetRotation = rotation;
sCryMeterNeedle->moveIncrement = NEEDLE_MOVE_INCREMENT;
2018-11-28 17:14:32 +01:00
}
2019-04-02 00:31:10 +02:00