pokeemerald/src/walda_phrase.c

279 lines
8.3 KiB
C
Raw Normal View History

2017-10-20 18:52:01 +02:00
#include "global.h"
2017-10-20 20:39:00 +02:00
#include "walda_phrase.h"
2017-10-20 18:52:01 +02:00
#include "string_util.h"
#include "event_data.h"
#include "naming_screen.h"
#include "main.h"
#include "text.h"
#include "new_game.h"
#include "overworld.h"
2018-12-19 22:47:27 +01:00
#include "pokemon_storage_system.h"
2018-12-20 22:53:08 +01:00
#include "field_screen_effect.h"
2017-10-20 18:52:01 +02:00
extern const u8 gText_Peekaboo[];
2017-10-20 20:39:00 +02:00
static void CB2_HandleGivenWaldaPhrase(void);
2021-09-26 22:20:39 +02:00
static u32 GetWaldaPhraseInputCase(u8 *);
static bool32 TryCalculateWallpaper(u16 *, u16 *, u8 *, u8 *, u16, u8 *);
static void SetWallpaperDataFromLetter(u8 *, u8 *, u32, u32, u32);
static u32 GetWallpaperDataBits(u8 *, u32, u32);
static void RotateWallpaperDataLeft(u8 *, s32, s32);
static void MaskWallpaperData(u8 *, u32, u8);
// There are 32 (2^5) unique letters allowed in a successful phrase for Walda.
#define BITS_PER_LETTER 5
// The letters allowed in a successful phrase for Walda
// All vowels are excluded, as well as X/x, Y/y, l, r, t, v, w, and z.
static const u8 sWaldaLettersTable[1 << BITS_PER_LETTER] =
2017-10-20 18:52:01 +02:00
{
CHAR_B, CHAR_C, CHAR_D, CHAR_F, CHAR_G, CHAR_H, CHAR_J, CHAR_K, CHAR_L, CHAR_M, CHAR_N, CHAR_P, CHAR_Q, CHAR_R, CHAR_S, CHAR_T, CHAR_V, CHAR_W, CHAR_Z,
CHAR_b, CHAR_c, CHAR_d, CHAR_f, CHAR_g, CHAR_h, CHAR_j, CHAR_k, CHAR_m, CHAR_n, CHAR_p, CHAR_q, CHAR_s
};
enum
{
2021-09-26 22:20:39 +02:00
PHRASE_CHANGED,
2017-10-20 18:52:01 +02:00
PHRASE_NO_CHANGE,
2021-09-26 22:20:39 +02:00
PHRASE_EMPTY
2017-10-20 18:52:01 +02:00
};
2017-10-20 20:46:26 +02:00
u16 TryBufferWaldaPhrase(void)
{
if (IsWaldaPhraseEmpty())
return FALSE;
StringCopy(gStringVar1, GetWaldaPhrasePtr());
return TRUE;
}
2017-10-20 18:52:01 +02:00
void DoWaldaNamingScreen(void)
{
StringCopy(gStringVar2, GetWaldaPhrasePtr());
DoNamingScreen(NAMING_SCREEN_WALDA, gStringVar2, 0, 0, 0, CB2_HandleGivenWaldaPhrase);
}
2017-10-20 20:39:00 +02:00
static void CB2_HandleGivenWaldaPhrase(void)
2017-10-20 18:52:01 +02:00
{
gSpecialVar_0x8004 = GetWaldaPhraseInputCase(gStringVar2);
switch (gSpecialVar_0x8004)
{
2021-09-26 22:20:39 +02:00
case PHRASE_EMPTY:
// If saved phrase is also empty, set default phrase
// Otherwise keep saved phrase
2017-10-20 18:52:01 +02:00
if (IsWaldaPhraseEmpty())
SetWaldaPhrase(gText_Peekaboo);
else
gSpecialVar_0x8004 = PHRASE_NO_CHANGE;
break;
2021-09-26 22:20:39 +02:00
case PHRASE_CHANGED:
2017-10-20 18:52:01 +02:00
SetWaldaPhrase(gStringVar2);
break;
case PHRASE_NO_CHANGE:
break;
}
StringCopy(gStringVar1, GetWaldaPhrasePtr());
2019-12-17 09:24:44 +01:00
gFieldCallback = FieldCB_ContinueScriptHandleMusic;
2018-02-14 00:58:22 +01:00
SetMainCallback2(CB2_ReturnToField);
2017-10-20 18:52:01 +02:00
}
2017-10-20 20:39:00 +02:00
static u32 GetWaldaPhraseInputCase(u8 *inputPtr)
2017-10-20 18:52:01 +02:00
{
2021-09-26 22:20:39 +02:00
// No input given
2017-10-20 18:52:01 +02:00
if (inputPtr[0] == EOS)
2021-09-26 22:20:39 +02:00
return PHRASE_EMPTY;
// Input given is the same as saved phrase
2017-10-20 18:52:01 +02:00
if (StringCompare(inputPtr, GetWaldaPhrasePtr()) == 0)
return PHRASE_NO_CHANGE;
2021-09-26 22:20:39 +02:00
// Input is new phrase
return PHRASE_CHANGED;
2017-10-20 18:52:01 +02:00
}
u16 TryGetWallpaperWithWaldaPhrase(void)
{
u16 backgroundClr, foregroundClr;
u8 patternId, iconId;
2018-11-01 21:31:10 +01:00
u16 trainerId = GetTrainerId(gSaveBlock2Ptr->playerTrainerId);
2017-11-11 01:12:18 +01:00
gSpecialVar_Result = TryCalculateWallpaper(&backgroundClr, &foregroundClr, &iconId, &patternId, trainerId, GetWaldaPhrasePtr());
2017-10-20 18:52:01 +02:00
2017-11-11 01:12:18 +01:00
if (gSpecialVar_Result)
2017-10-20 18:52:01 +02:00
{
SetWaldaWallpaperPatternId(patternId);
SetWaldaWallpaperIconId(iconId);
SetWaldaWallpaperColors(backgroundClr, foregroundClr);
}
2017-11-11 01:12:18 +01:00
SetWaldaWallpaperLockedOrUnlocked(gSpecialVar_Result);
2021-09-26 22:20:39 +02:00
return (bool8)gSpecialVar_Result;
2017-10-20 18:52:01 +02:00
}
2017-10-20 20:39:00 +02:00
static u8 GetLetterTableId(u8 letter)
2017-10-20 18:52:01 +02:00
{
s32 i;
for (i = 0; i < ARRAY_COUNT(sWaldaLettersTable); i++)
{
if (sWaldaLettersTable[i] == letter)
return i;
}
return ARRAY_COUNT(sWaldaLettersTable);
}
2021-09-26 22:20:39 +02:00
// Attempts to generate a wallpaper based on the given trainer id and phrase.
// Returns TRUE if successful and sets the wallpaper results to the given pointers.
// Returns FALSE if no wallpaper was generated (Walda "didn't like" the phrase).
// A 9-byte array is used to calculate the wallpaper's data.
// The elements of this array are defined below.
#define BG_COLOR_LO data[0]
#define BG_COLOR_HI data[1]
#define FG_COLOR_LO data[2]
#define FG_COLOR_HI data[3]
#define ICON_ID data[4]
#define PATTERN_ID data[5]
#define TID_CHECK_HI data[6]
#define TID_CHECK_LO data[7]
#define KEY data[8]
#define NUM_WALLPAPER_DATA_BYTES 9
#define TO_BIT_OFFSET(i) (3 + (8 * (i))) // Convert a position in the phrase to a bit number into the wallpaper data array
2017-10-20 20:39:00 +02:00
static bool32 TryCalculateWallpaper(u16* backgroundClr, u16 *foregroundClr, u8 *iconId, u8 *patternId, u16 trainerId, u8 *phrase)
2017-10-20 18:52:01 +02:00
{
s32 i;
2021-09-26 22:20:39 +02:00
ALIGNED(2) u8 data[NUM_WALLPAPER_DATA_BYTES];
u8 charsByTableId[WALDA_PHRASE_LENGTH];
2017-10-20 18:52:01 +02:00
u16 *ptr;
2021-09-26 22:20:39 +02:00
// Reject any phrase that does not use the full length
if (StringLength(phrase) != WALDA_PHRASE_LENGTH)
2017-10-20 18:52:01 +02:00
return FALSE;
2021-09-26 22:20:39 +02:00
// Reject any phrase that uses characters not in sWaldaLettersTable
for (i = 0; i < WALDA_PHRASE_LENGTH; i++)
2017-10-20 18:52:01 +02:00
{
charsByTableId[i] = GetLetterTableId(phrase[i]);
if (charsByTableId[i] == ARRAY_COUNT(sWaldaLettersTable))
return FALSE;
}
2021-09-26 22:20:39 +02:00
// Use the given phrase to populate the wallpaper data array
// The data array is 9 bytes (72 bits) long, and each letter contributes to 5 bits of the array
// Because the phrase is 15 letters long there are 75 bits from the phrase to distribute
// Therefore the last letter contributes to the last 2 bits of the array, and the remaining 3 bits wrap around
for (i = 0; i < WALDA_PHRASE_LENGTH - 1; i++)
SetWallpaperDataFromLetter(data, charsByTableId, BITS_PER_LETTER * i, TO_BIT_OFFSET(i), BITS_PER_LETTER);
2017-10-20 18:52:01 +02:00
2021-09-26 22:20:39 +02:00
// Do first 2 bits of the last letter
SetWallpaperDataFromLetter(data, charsByTableId, BITS_PER_LETTER * (WALDA_PHRASE_LENGTH - 1), TO_BIT_OFFSET(WALDA_PHRASE_LENGTH - 1), 2);
2017-10-20 18:52:01 +02:00
2021-09-26 22:20:39 +02:00
// Check the first 3 bits of the data array against the remaining 3 bits of the last letter
// Reject the phrase if they are not already the same
if (GetWallpaperDataBits(data, 0, 3) != GetWallpaperDataBits(charsByTableId, TO_BIT_OFFSET(WALDA_PHRASE_LENGTH - 1) + 2, 3))
2017-10-20 18:52:01 +02:00
return FALSE;
2021-09-26 22:20:39 +02:00
// Perform some relatively arbitrary changes to the wallpaper data using the last byte (KEY)
RotateWallpaperDataLeft(data, NUM_WALLPAPER_DATA_BYTES, 21);
RotateWallpaperDataLeft(data, NUM_WALLPAPER_DATA_BYTES - 1, KEY & 0xF);
MaskWallpaperData(data, NUM_WALLPAPER_DATA_BYTES - 1, KEY >> 4);
2017-10-20 18:52:01 +02:00
2021-09-26 22:20:39 +02:00
// Reject the results of any phrase that are 'incompatible' with the player's trainer id
if (TID_CHECK_HI != (BG_COLOR_LO ^ FG_COLOR_LO ^ ICON_ID ^ (trainerId >> 8)))
2017-10-20 18:52:01 +02:00
return FALSE;
2021-09-26 22:20:39 +02:00
if (TID_CHECK_LO != (BG_COLOR_HI ^ FG_COLOR_HI ^ PATTERN_ID ^ (trainerId & 0xFF)))
2017-10-20 18:52:01 +02:00
return FALSE;
2021-09-26 22:20:39 +02:00
// Successful phrase, save resulting wallpaper
ptr = (u16*) &BG_COLOR_LO;
2017-10-20 18:52:01 +02:00
*backgroundClr = *ptr;
2021-09-26 22:20:39 +02:00
ptr = (u16*) &FG_COLOR_LO;
2017-10-20 18:52:01 +02:00
*foregroundClr = *ptr;
2021-09-26 22:20:39 +02:00
*iconId = ICON_ID;
*patternId = PATTERN_ID;
2017-10-20 18:52:01 +02:00
return TRUE;
}
2021-09-26 22:20:39 +02:00
static void RotateWallpaperDataLeft(u8 *data, s32 size, s32 numShifts)
2017-10-20 18:52:01 +02:00
{
s32 i, j;
2021-09-26 22:20:39 +02:00
u8 temp1, temp2;
2017-10-20 18:52:01 +02:00
2021-09-26 22:20:39 +02:00
for (i = numShifts - 1; i != -1; i--)
2017-10-20 18:52:01 +02:00
{
2021-09-26 22:20:39 +02:00
temp1 = (data[0] & (1 << 7)) >> 7;
2017-10-20 18:52:01 +02:00
2021-09-26 22:20:39 +02:00
for (j = size - 1; j >= 0; j--)
2017-10-20 18:52:01 +02:00
{
2021-09-26 22:20:39 +02:00
temp2 = (data[j] & (1 << 7)) >> 7;
data[j] <<= 1;
data[j] |= temp1;
temp1 = temp2;
2017-10-20 18:52:01 +02:00
}
}
}
2017-10-20 20:39:00 +02:00
2021-09-26 22:20:39 +02:00
static void MaskWallpaperData(u8 *data, u32 size, u8 mask)
2017-10-20 20:39:00 +02:00
{
u32 i;
2021-09-26 22:20:39 +02:00
mask |= (mask << 4);
2017-10-20 20:39:00 +02:00
2021-09-26 22:20:39 +02:00
for (i = 0; i < size; i++)
data[i] ^= mask;
2017-10-20 20:39:00 +02:00
}
2021-09-26 22:20:39 +02:00
static bool8 GetWallpaperDataBit(u8 *data, u32 bitNum)
2017-10-20 20:39:00 +02:00
{
2021-09-26 22:20:39 +02:00
u32 i = bitNum / 8;
u32 flag = (1 << 7) >> (bitNum % 8);
2017-10-20 20:39:00 +02:00
2021-09-26 22:20:39 +02:00
return (data[i] & flag) != 0;
2017-10-20 20:39:00 +02:00
}
2021-09-26 22:20:39 +02:00
static void SetWallpaperDataBit(u8 *data, u32 bitNum)
2017-10-20 20:39:00 +02:00
{
2021-09-26 22:20:39 +02:00
u32 i = bitNum / 8;
u8 flag = (1 << 7) >> (bitNum % 8);
2017-10-20 20:39:00 +02:00
2021-09-26 22:20:39 +02:00
data[i] |= flag;
2017-10-20 20:39:00 +02:00
}
2021-09-26 22:20:39 +02:00
static void ClearWallpaperDataBit(u8 *data, u32 bitNum)
2017-10-20 20:39:00 +02:00
{
2021-09-26 22:20:39 +02:00
u32 i = bitNum / 8;
u8 mask = ~((1 << 7) >> (bitNum % 8));
2017-10-20 20:39:00 +02:00
2021-09-26 22:20:39 +02:00
data[i] &= mask;
2017-10-20 20:39:00 +02:00
}
2021-09-26 22:20:39 +02:00
static void SetWallpaperDataFromLetter(u8 *data, u8 *letterTableIds, u32 setOffset, u32 getOffset, u32 numBits)
2017-10-20 20:39:00 +02:00
{
u32 i;
2021-09-26 22:20:39 +02:00
for (i = 0; i < numBits; i++)
2017-10-20 20:39:00 +02:00
{
2021-09-26 22:20:39 +02:00
if (GetWallpaperDataBit(letterTableIds, getOffset + i))
SetWallpaperDataBit(data, setOffset + i);
2017-10-20 20:39:00 +02:00
else
2021-09-26 22:20:39 +02:00
ClearWallpaperDataBit(data, setOffset + i);
2017-10-20 20:39:00 +02:00
}
}
2021-09-26 22:20:39 +02:00
static u32 GetWallpaperDataBits(u8 *data, u32 offset, u32 numBits)
2017-10-20 20:39:00 +02:00
{
2021-09-26 22:20:39 +02:00
u32 bits, i;
2017-10-20 20:39:00 +02:00
2021-09-26 22:20:39 +02:00
for (bits = 0, i = 0; i < numBits; i++)
2017-10-20 20:39:00 +02:00
{
2021-09-26 22:20:39 +02:00
bits <<= 1;
bits |= GetWallpaperDataBit(data, offset + i);
2017-10-20 20:39:00 +02:00
}
2021-09-26 22:20:39 +02:00
return bits;
2017-10-20 20:39:00 +02:00
}