#include "global.h" #include "battle.h" #include "battle_message.h" #include "battle_tower.h" #include "item.h" #include "event_data.h" #include "constants/items.h" #include "pokemon.h" #include "data2.h" #include "text.h" #include "string_util.h" #include "link.h" #include "battle_setup.h" #include "battle_tower.h" #include "constants/flags.h" #include "ewram.h" #define BATTLESTRING_TO_SUB 12 #define BATTLESTRINGS_NO 351 #define BATTLESTRINGS_MAX BATTLESTRINGS_NO + BATTLESTRING_TO_SUB #ifdef GERMAN #include "data/battle_strings_de.h" // TODO: German #else #include "data/battle_strings_en.h" #endif // This is four lists of moves which use a different attack string in Japanese // to the default. See the documentation for ChooseTypeOfMoveUsedString for more detail. const u16 gUnknown_084016BC[] = { MOVE_SWORDS_DANCE, MOVE_STRENGTH, MOVE_GROWTH, MOVE_HARDEN, MOVE_MINIMIZE, MOVE_SMOKESCREEN, MOVE_WITHDRAW, MOVE_DEFENSE_CURL, MOVE_EGG_BOMB, MOVE_SMOG, MOVE_BONE_CLUB, MOVE_FLASH, MOVE_SPLASH, MOVE_ACID_ARMOR, MOVE_BONEMERANG, MOVE_REST, MOVE_SHARPEN, MOVE_SUBSTITUTE, MOVE_MIND_READER, MOVE_SNORE, MOVE_PROTECT, MOVE_SPIKES, MOVE_ENDURE, MOVE_ROLLOUT, MOVE_SWAGGER, MOVE_SLEEP_TALK, MOVE_HIDDEN_POWER, MOVE_PSYCH_UP, MOVE_EXTREME_SPEED, MOVE_FOLLOW_ME, MOVE_TRICK, MOVE_ASSIST, MOVE_INGRAIN, MOVE_KNOCK_OFF, MOVE_CAMOUFLAGE, MOVE_ASTONISH, MOVE_ODOR_SLEUTH, MOVE_GRASS_WHISTLE, MOVE_SHEER_COLD, MOVE_MUDDY_WATER, MOVE_IRON_DEFENSE, MOVE_BOUNCE, 0, MOVE_TELEPORT, MOVE_RECOVER, MOVE_BIDE, MOVE_AMNESIA, MOVE_FLAIL, MOVE_TAUNT, MOVE_BULK_UP, 0, MOVE_MEDITATE, MOVE_AGILITY, MOVE_MIMIC, MOVE_DOUBLE_TEAM, MOVE_BARRAGE, MOVE_TRANSFORM, MOVE_STRUGGLE, MOVE_SCARY_FACE, MOVE_CHARGE, MOVE_WISH, MOVE_BRICK_BREAK, MOVE_YAWN, MOVE_FEATHER_DANCE, MOVE_TEETER_DANCE, MOVE_MUD_SPORT, MOVE_FAKE_TEARS, MOVE_WATER_SPORT, MOVE_CALM_MIND, 0, MOVE_POUND, MOVE_SCRATCH, MOVE_VICE_GRIP, MOVE_WING_ATTACK, MOVE_FLY, MOVE_BIND, MOVE_SLAM, MOVE_HORN_ATTACK, MOVE_WRAP, MOVE_THRASH, MOVE_TAIL_WHIP, MOVE_LEER, MOVE_BITE, MOVE_GROWL, MOVE_ROAR, MOVE_SING, MOVE_PECK, MOVE_ABSORB, MOVE_STRING_SHOT, MOVE_EARTHQUAKE, MOVE_FISSURE, MOVE_DIG, MOVE_TOXIC, MOVE_SCREECH, MOVE_METRONOME, MOVE_LICK, MOVE_CLAMP, MOVE_CONSTRICT, MOVE_POISON_GAS, MOVE_BUBBLE, MOVE_SLASH, MOVE_SPIDER_WEB, MOVE_NIGHTMARE, MOVE_CURSE, MOVE_FORESIGHT, MOVE_CHARM, MOVE_ATTRACT, MOVE_ROCK_SMASH, MOVE_UPROAR, MOVE_SPIT_UP, MOVE_SWALLOW, MOVE_TORMENT, MOVE_FLATTER, MOVE_ROLE_PLAY, MOVE_ENDEAVOR, MOVE_TICKLE, MOVE_COVET, 0, }; const u8 gUnknown_084017A8[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // empty flags extern const u8* const gBattleStringsTable[BATTLESTRINGS_NO]; extern u16 gLastUsedItem; extern u8 gLastUsedAbility; extern u8 gActiveBattler; extern u8 gBattlerAttacker; extern u8 gBattlerTarget; extern u8 gPotentialItemEffectBattler; extern u8 gEffectBattler; extern u8 gBattleTextBuff1[]; extern u8 gBattleTextBuff2[]; extern u8 gBattleTextBuff3[]; extern u8 gStringVar1[]; extern u8 gStringVar2[]; extern u8 gStringVar3[]; extern u16 gBattleTypeFlags; extern u16 gTrainerBattleOpponent; extern u8 gDisplayedStringBattle[]; extern u8 gStringVar1[]; extern u8 gStringVar2[]; extern u8 gStringVar3[]; extern u16 gBattlerPartyIndexes[4]; extern struct BattleEnigmaBerry gEnigmaBerries[4]; extern u8 gBattleBufferA[4][0x200]; EWRAM_DATA u8 gAbilitiesPerBank[4] = {0}; extern const u8* const gUnknown_08401674[]; // table of pointers to 'a -TYPE' strings extern const u8* const gUnknown_08400F58[]; // table of pointers to stat strings extern const u8* const gUnknown_08400F78[]; // table of pointers to flavour strings struct StatusFlagString { u8* flag; u8* ptr; }; extern const struct StatusFlagString gUnknown_081FA6D4[7]; // status flag/text extern struct StringInfoBattle* gSelectedOrderFromParty; #define gStringInfo gSelectedOrderFromParty void ChooseMoveUsedParticle(u8* textBuff); void ChooseTypeOfMoveUsedString(u8* textBuff); void ExpandBattleTextBuffPlaceholders(u8* src, u8* dst); u8 GetBattlerSide(u8 bank); s32 sub_803FC34(u16); void get_trainer_name(u8* dst); u8 get_trainer_class_name_index(void); u8 GetMultiplayerId(void); u8 GetBattlerAtPosition(u8 ID); u8 GetBattlerSide(u8 bank); u8 GetBattlerPosition(u8 bank); #ifdef GERMAN extern u8 *de_sub_804110C(); #endif void BufferStringBattle(u16 stringID) { int i; const u8* stringPtr = NULL; gStringInfo = (struct StringInfoBattle*)(&gBattleBufferA[gActiveBattler][4]); gLastUsedItem = gStringInfo->lastItem; gLastUsedAbility = gStringInfo->lastAbility; gBattleStruct->scriptingActive = gStringInfo->scrActive; gBattleStruct->unk1605E = gStringInfo->unk1605E; gBattleStruct->hpScale = gStringInfo->hpScale; gPotentialItemEffectBattler = gStringInfo->StringBank; gBattleStruct->stringMoveType = gStringInfo->moveType; for (i = 0; i < 4; i++) { gAbilitiesPerBank[i] = gStringInfo->abilities[i]; } for (i = 0; i < 0x10; i++) { gBattleTextBuff1[i] = gStringInfo->textBuffs[0][i]; gBattleTextBuff2[i] = gStringInfo->textBuffs[1][i]; gBattleTextBuff3[i] = gStringInfo->textBuffs[2][i]; } switch (stringID) { case 0: // first battle msg if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) { if (gBattleTypeFlags & BATTLE_TYPE_MULTI) stringPtr = BattleText_DoubleWantToBattle; else stringPtr = BattleText_SingleWantToBattle2; } else { stringPtr = BattleText_SingleWantToBattle1; #ifdef GERMAN stringPtr = de_sub_804110C(0xFFFF, stringPtr); #endif } } else { if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY) stringPtr = BattleText_WildAppeared2; else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) // interesting, looks like they had something planned for wild double battles stringPtr = BattleText_WildDoubleAppeared; else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL) stringPtr = BattleText_WildAppeared3; else stringPtr = BattleText_WildAppeared1; } break; case 1: // poke first send-out if (GetBattlerSide(gActiveBattler) == 0) { if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { if (gBattleTypeFlags & BATTLE_TYPE_MULTI) stringPtr = BattleText_SentOutSingle11; else stringPtr = BattleText_SentOutDouble4; } else stringPtr = BattleText_SentOutSingle6; } else { if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { if (gBattleTypeFlags & BATTLE_TYPE_MULTI) stringPtr = BattleText_SentOutDouble3; else if (gBattleTypeFlags & BATTLE_TYPE_LINK) stringPtr = BattleText_SentOutDouble2; else { stringPtr = BattleText_SentOutDouble1; #ifdef GERMAN stringPtr = de_sub_804110C(0xFFFF, stringPtr); #endif } } else if (gBattleTypeFlags & BATTLE_TYPE_LINK) stringPtr = BattleText_SentOutSingle3; else { stringPtr = BattleText_SentOutSingle1; #ifdef GERMAN stringPtr = de_sub_804110C(0xFFFF, stringPtr); #endif } } break; case 2: // sending poke to ball msg if (GetBattlerSide(gActiveBattler) == 0) { if (gBattleStruct->hpScale == 0) stringPtr = BattleText_ComeBackSingle1; else if (gBattleStruct->hpScale == 1 || gBattleTypeFlags & BATTLE_TYPE_DOUBLE) stringPtr = BattleText_ComeBackSingle2; else if (gBattleStruct->hpScale == 2) stringPtr = BattleText_ComeBackSingle3; else stringPtr = BattleText_ComeBackSingle4; } else { if (gTrainerBattleOpponent == LINK_BATTLE_OPPONENT) { if (gBattleTypeFlags & BATTLE_TYPE_MULTI) stringPtr = BattleText_WithdrewPoke3; else stringPtr = BattleText_WithdrewPoke2; } else { stringPtr = BattleText_WithdrewPoke1; #ifdef GERMAN stringPtr = de_sub_804110C(0xFFFF, stringPtr); #endif } } break; case 3: // switch-in msg if (GetBattlerSide(gBattleStruct->scriptingActive) == 0) { if (gBattleStruct->hpScale == 0 || gBattleTypeFlags & BATTLE_TYPE_DOUBLE) stringPtr = BattleText_SentOutSingle7; else if (gBattleStruct->hpScale == 1) stringPtr = BattleText_SentOutSingle8; else if (gBattleStruct->hpScale == 2) stringPtr = BattleText_SentOutSingle9; else stringPtr = BattleText_SentOutSingle10; } else { if (gBattleTypeFlags & BATTLE_TYPE_LINK) { if (gBattleTypeFlags & BATTLE_TYPE_MULTI) stringPtr = BattleText_SentOutSingle5; else stringPtr = BattleText_SentOutSingle4; } else { stringPtr = BattleText_SentOutSingle2; #ifdef GERMAN stringPtr = de_sub_804110C(0xFFFF, stringPtr); #endif } } break; case 4: // pokemon used a move msg ChooseMoveUsedParticle(gBattleTextBuff1); if (gStringInfo->currentMove > 0x162) StringCopy(gBattleTextBuff2, gUnknown_08401674[gBattleStruct->stringMoveType]); else StringCopy(gBattleTextBuff2, gMoveNames[gStringInfo->currentMove]); ChooseTypeOfMoveUsedString(gBattleTextBuff2); stringPtr = BattleText_OpponentUsedMove; break; case 5: // battle end if (gBattleTextBuff1[0] & 0x80) { gBattleTextBuff1[0] &= ~(0x80); if (GetBattlerSide(gActiveBattler) == 1 && gBattleTextBuff1[0] != 3) gBattleTextBuff1[0] ^= 3; if (gBattleTextBuff1[0] == B_OUTCOME_LOST || gBattleTextBuff1[0] == B_OUTCOME_DREW) stringPtr = BattleText_GotAwaySafely; else { if (gBattleTypeFlags & BATTLE_TYPE_MULTI) stringPtr = BattleText_FledDouble; else stringPtr = BattleText_FledSingle; } } else { if (GetBattlerSide(gActiveBattler) == 1 && gBattleTextBuff1[0] != 3) gBattleTextBuff1[0] ^= 3; if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { switch (gBattleTextBuff1[0]) { case B_OUTCOME_WON: stringPtr = BattleText_PlayerDefeatedTrainers; break; case B_OUTCOME_LOST: stringPtr = BattleText_PlayerLostTrainers; break; case B_OUTCOME_DREW: stringPtr = BattleText_PlayerTiedTrainers; break; } } else { switch (gBattleTextBuff1[0]) { case B_OUTCOME_WON: stringPtr = BattleText_PlayerDefeatedTrainer; break; case B_OUTCOME_LOST: stringPtr = BattleText_PlayerLostTrainer; break; case B_OUTCOME_DREW: stringPtr = BattleText_PlayerTiedTrainer; break; } } } break; default: // load a string from the table if (stringID >= BATTLESTRINGS_MAX) { gDisplayedStringBattle[0] = EOS; return; } else { stringPtr = gBattleStringsTable[stringID - BATTLESTRING_TO_SUB]; #ifdef GERMAN stringPtr = de_sub_804110C(stringID, stringPtr); #endif } break; } BattleStringExpandPlaceholdersToDisplayedString(stringPtr); } u32 BattleStringExpandPlaceholdersToDisplayedString(const u8* src) { BattleStringExpandPlaceholders(src, gDisplayedStringBattle); } const u8* TryGetStatusString(u8* src) { u32 i; u8 status[8]; u32 flag1, flag2; u8* statusPtr; memcpy(status, gUnknown_084017A8, 8); statusPtr = status; for (i = 0; i < sizeof(struct StatusFlagString); i++) { if (*src == EOS) break; // oneline required to match with -g *statusPtr = *src; src++; statusPtr++; } flag1 = *(u32*)(&status[0]); flag2 = *(u32*)(&status[4]); for (i = 0; i < 7; i++) { if (flag1 == *(u32*)(&gUnknown_081FA6D4[i].flag[0]) && flag2 == *(u32*)(&gUnknown_081FA6D4[i].flag[4])) return gUnknown_081FA6D4[i].ptr; } return NULL; } #ifdef GERMAN extern u8 *de_sub_8073174(u8 *, const u8 *); extern u8 *de_sub_8041024(s32, u32); #endif #ifdef ENGLISH #define HANDLE_NICKNAME_STRING_CASE(bank, monIndex) \ if (GetBattlerSide(bank) != 0) \ { \ if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) \ toCpy = BattleText_Foe; \ else \ toCpy = BattleText_Wild; \ while (*toCpy != EOS) \ { \ dst[dstID] = *toCpy; \ dstID++; \ toCpy++; \ } \ GetMonData(&gEnemyParty[monIndex], MON_DATA_NICKNAME, text); \ } \ else \ { \ GetMonData(&gPlayerParty[monIndex], MON_DATA_NICKNAME, text); \ } \ StringGetEnd10(text); \ toCpy = text; #else #define HANDLE_NICKNAME_STRING_CASE(bank, monIndex) \ if (GetBattlerSide(bank) != 0) \ { \ GetMonData(&gEnemyParty[monIndex], MON_DATA_NICKNAME, text); \ StringGetEnd10(text); \ toCpy = text; \ while (*toCpy != EOS) \ { \ dst[dstID] = *toCpy; \ dstID++; \ toCpy++; \ } \ if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) \ toCpy = BattleText_Foe; \ else \ toCpy = BattleText_Wild; \ } \ else \ { \ GetMonData(&gPlayerParty[monIndex], MON_DATA_NICKNAME, text); \ StringGetEnd10(text); \ toCpy = text; \ } #endif u32 BattleStringExpandPlaceholders(const u8* src, u8* dst) { u32 dstID = 0; // if they used dstID, why not use srcID as well? const u8* toCpy = NULL; u8 text[12]; u8 multiplayerID = GetMultiplayerId(); while (*src != EOS) { if (*src == B_BUFF_PLACEHOLDER_BEGIN) { src++; switch (*src) { case B_TXT_BUFF1: if (gBattleTextBuff1[0] == B_BUFF_PLACEHOLDER_BEGIN) { ExpandBattleTextBuffPlaceholders(gBattleTextBuff1, gStringVar1); toCpy = gStringVar1; } else { toCpy = TryGetStatusString(gBattleTextBuff1); if (toCpy == NULL) toCpy = gBattleTextBuff1; } break; case B_TXT_BUFF2: if (gBattleTextBuff2[0] == B_BUFF_PLACEHOLDER_BEGIN) { ExpandBattleTextBuffPlaceholders(gBattleTextBuff2, gStringVar2); toCpy = gStringVar2; } else toCpy = gBattleTextBuff2; break; case B_TXT_BUFF3: if (gBattleTextBuff3[0] == B_BUFF_PLACEHOLDER_BEGIN) { ExpandBattleTextBuffPlaceholders(gBattleTextBuff3, gStringVar3); toCpy = gStringVar3; } else toCpy = gBattleTextBuff3; break; case B_TXT_PLAYER_MON1_NAME: // first player poke name GetMonData(&gPlayerParty[gBattlerPartyIndexes[GetBattlerAtPosition(0)]], MON_DATA_NICKNAME, text); StringGetEnd10(text); toCpy = text; break; case B_TXT_OPPONENT_MON1_NAME: // first enemy poke name GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetBattlerAtPosition(1)]], MON_DATA_NICKNAME, text); StringGetEnd10(text); toCpy = text; break; case B_TXT_PLAYER_MON2_NAME: // second player poke name GetMonData(&gPlayerParty[gBattlerPartyIndexes[GetBattlerAtPosition(2)]], MON_DATA_NICKNAME, text); StringGetEnd10(text); toCpy = text; break; case B_TXT_OPPONENT_MON2_NAME: // second enemy poke name GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetBattlerAtPosition(3)]], MON_DATA_NICKNAME, text); StringGetEnd10(text); toCpy = text; break; case B_TXT_LINK_PLAYER_MON1_NAME: // link first player poke name GetMonData(&gPlayerParty[gBattlerPartyIndexes[gLinkPlayers[multiplayerID].id]], MON_DATA_NICKNAME, text); StringGetEnd10(text); toCpy = text; break; case B_TXT_LINK_OPPONENT_MON1_NAME: // link first opponent poke name GetMonData(&gEnemyParty[gBattlerPartyIndexes[gLinkPlayers[multiplayerID].id ^ 1]], MON_DATA_NICKNAME, text); StringGetEnd10(text); toCpy = text; break; case B_TXT_LINK_PLAYER_MON2_NAME: // link second player poke name GetMonData(&gPlayerParty[gBattlerPartyIndexes[gLinkPlayers[multiplayerID].id ^ 2]], MON_DATA_NICKNAME, text); StringGetEnd10(text); toCpy = text; break; case B_TXT_LINK_OPPONENT_MON2_NAME: // link second opponent poke name GetMonData(&gEnemyParty[gBattlerPartyIndexes[gLinkPlayers[multiplayerID].id ^ 3]], MON_DATA_NICKNAME, text); StringGetEnd10(text); toCpy = text; break; case B_TXT_ATK_NAME_WITH_PREFIX_MON1: // attacker name with prefix, only bank 0/1 HANDLE_NICKNAME_STRING_CASE(gBattlerAttacker, gBattlerPartyIndexes[GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) & 1)]) break; case B_TXT_ATK_PARTNER_NAME: // attacker partner name, only bank 0/1 if (GetBattlerSide(gBattlerAttacker) == 0) GetMonData(&gPlayerParty[gBattlerPartyIndexes[GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) & 1) + 2]], MON_DATA_NICKNAME, text); else GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) & 1) + 2]], MON_DATA_NICKNAME, text); StringGetEnd10(text); toCpy = text; break; case B_TXT_ATK_NAME_WITH_PREFIX: // attacker name with prefix HANDLE_NICKNAME_STRING_CASE(gBattlerAttacker, gBattlerPartyIndexes[gBattlerAttacker]) break; case B_TXT_DEF_NAME_WITH_PREFIX: // target name with prefix HANDLE_NICKNAME_STRING_CASE(gBattlerTarget, gBattlerPartyIndexes[gBattlerTarget]) break; case B_TXT_EFF_NAME_WITH_PREFIX: // effect bank name with prefix HANDLE_NICKNAME_STRING_CASE(gEffectBattler, gBattlerPartyIndexes[gEffectBattler]) break; case B_TXT_ACTIVE_NAME_WITH_PREFIX: // active bank name with prefix HANDLE_NICKNAME_STRING_CASE(gActiveBattler, gBattlerPartyIndexes[gActiveBattler]) break; case B_TXT_SCR_ACTIVE_NAME_WITH_PREFIX: // scripting active bank name with prefix HANDLE_NICKNAME_STRING_CASE(gBattleStruct->scriptingActive, gBattlerPartyIndexes[gBattleStruct->scriptingActive]) break; case B_TXT_CURRENT_MOVE: // current move name if (gStringInfo->currentMove >= NUM_MOVES) toCpy = (void*) &gUnknown_08401674[gBattleStruct->stringMoveType]; else toCpy = gMoveNames[gStringInfo->currentMove]; break; case B_TXT_LAST_MOVE: // last used move name if (gStringInfo->lastMove >= NUM_MOVES) toCpy = (void*) &gUnknown_08401674[gBattleStruct->stringMoveType]; else toCpy = gMoveNames[gStringInfo->lastMove]; break; case B_TXT_LAST_ITEM: // last used item if (gBattleTypeFlags & BATTLE_TYPE_LINK) { if (gLastUsedItem == ITEM_ENIGMA_BERRY) { if (gLinkPlayers[gBattleStruct->multiplayerId].id == gPotentialItemEffectBattler) { StringCopy(text, gEnigmaBerries[gPotentialItemEffectBattler].name); #ifdef ENGLISH StringAppend(text, BattleText_Berry); #else de_sub_8073174(text, BattleText_Berry); #endif toCpy = text; } else toCpy = BattleText_EnigmaBerry; } else { CopyItemName(gLastUsedItem, text); toCpy = text; } } else { CopyItemName(gLastUsedItem, text); toCpy = text; } break; case B_TXT_LAST_ABILITY: // last used ability toCpy = gAbilityNames[gLastUsedAbility]; break; case B_TXT_ATK_ABILITY: // attacker ability toCpy = gAbilityNames[gAbilitiesPerBank[gBattlerAttacker]]; break; case B_TXT_DEF_ABILITY: // target ability toCpy = gAbilityNames[gAbilitiesPerBank[gBattlerTarget]]; break; case B_TXT_SCR_ACTIVE_ABILITY: // scripting active ability toCpy = gAbilityNames[gAbilitiesPerBank[gBattleStruct->scriptingActive]]; break; case B_TXT_EFF_ABILITY: // effect bank ability toCpy = gAbilityNames[gAbilitiesPerBank[gEffectBattler]]; break; case B_TXT_TRAINER1_CLASS: // trainer class name #ifdef ENGLISH if (gTrainerBattleOpponent == SECRET_BASE_OPPONENT) toCpy = gTrainerClassNames[GetSecretBaseTrainerNameIndex()]; else if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) toCpy = gTrainerClassNames[get_trainer_class_name_index()]; else if (gBattleTypeFlags & BATTLE_TYPE_EREADER_TRAINER) toCpy = gTrainerClassNames[GetEReaderTrainerClassNameIndex()]; else toCpy = gTrainerClassNames[gTrainers[gTrainerBattleOpponent].trainerClass]; break; #else if (gTrainerBattleOpponent == SECRET_BASE_OPPONENT) toCpy = de_sub_8041024(gTrainerBattleOpponent, 0); else if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) toCpy = de_sub_8041024(BATTLE_TYPE_BATTLE_TOWER, 0); else if (gBattleTypeFlags & BATTLE_TYPE_EREADER_TRAINER) toCpy = de_sub_8041024(BATTLE_TYPE_EREADER_TRAINER, 0); else toCpy = de_sub_8041024(0, gTrainerBattleOpponent); break; #endif case B_TXT_TRAINER1_NAME: // trainer name if (gTrainerBattleOpponent == SECRET_BASE_OPPONENT) { memset(text, 0xFF, 8); memcpy(text, eSecretBaseRecord->playerName, 7); toCpy = text; } else if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) { get_trainer_name(text); toCpy = text; } else if (gBattleTypeFlags & BATTLE_TYPE_EREADER_TRAINER) { SetEReaderTrainerName(text); toCpy = text; } else toCpy = gTrainers[gTrainerBattleOpponent].trainerName; break; case B_TXT_LINK_PLAYER_NAME: // link player name? toCpy = gLinkPlayers[multiplayerID].name; break; case B_TXT_LINK_PARTNER_NAME: // link partner name? toCpy = gLinkPlayers[sub_803FC34(2 ^ gLinkPlayers[multiplayerID].id)].name; break; case B_TXT_LINK_OPPONENT1_NAME: // link opponent 1 name? toCpy = gLinkPlayers[sub_803FC34(1 ^ gLinkPlayers[multiplayerID].id)].name; break; case B_TXT_LINK_OPPONENT2_NAME: // link opponent 2 name? toCpy = gLinkPlayers[sub_803FC34(3 ^ gLinkPlayers[multiplayerID].id)].name; break; case B_TXT_LINK_SCR_TRAINER_NAME: // link scripting active name toCpy = gLinkPlayers[sub_803FC34(gBattleStruct->scriptingActive)].name; break; case B_TXT_PLAYER_NAME: // player name toCpy = gSaveBlock2.playerName; break; case B_TXT_TRAINER1_LOSE_TEXT: // ? toCpy = GetTrainerLoseText(); break; case B_TXT_22: // ? HANDLE_NICKNAME_STRING_CASE(gBattleStruct->scriptingActive, gBattleStruct->unk1605E) break; case B_TXT_PC_CREATOR_NAME: // lanette pc if (FlagGet(FLAG_SYS_PC_LANETTE)) toCpy = BattleText_Lanette; else toCpy = BattleText_Someone; break; case B_TXT_ATK_PREFIX2: if (GetBattlerSide(gBattlerAttacker) == 0) toCpy = BattleText_Ally2; else toCpy = BattleText_Foe3; break; case B_TXT_DEF_PREFIX2: if (GetBattlerSide(gBattlerTarget) == 0) toCpy = BattleText_Ally2; else toCpy = BattleText_Foe3; break; case B_TXT_ATK_PREFIX1: if (GetBattlerSide(gBattlerAttacker) == 0) toCpy = BattleText_Ally; else toCpy = BattleText_Foe2; break; case B_TXT_DEF_PREFIX1: if (GetBattlerSide(gBattlerTarget) == 0) toCpy = BattleText_Ally; else toCpy = BattleText_Foe2; break; case B_TXT_ATK_PREFIX3: if (GetBattlerSide(gBattlerAttacker) == 0) toCpy = BattleText_Ally3; else toCpy = BattleText_Foe4; break; case B_TXT_DEF_PREFIX3: if (GetBattlerSide(gBattlerTarget) == 0) toCpy = BattleText_Ally3; else toCpy = BattleText_Foe4; break; } //if (toCpy != NULL) really GF, why did you forget about this? while (*toCpy != EOS) { dst[dstID] = *toCpy; dstID++; toCpy++; } if (*src == B_TXT_TRAINER1_LOSE_TEXT) { dst[dstID] = EXT_CTRL_CODE_BEGIN; dstID++; dst[dstID] = EXT_CTRL_CODE_PAUSE_UNTIL_PRESS; dstID++; } } else { dst[dstID] = *src; dstID++; } src++; } dst[dstID] = *src; dstID++; return dstID; } void ExpandBattleTextBuffPlaceholders(u8* src, u8* dst) { u32 srcID = 1; u32 value = 0; u8 text[12]; u16 hword; *dst = EOS; while (src[srcID] != EOS) { switch (src[srcID]) { case B_BUFF_STRING: // battle string hword = T1_READ_16(&src[srcID + 1]); #ifdef GERMAN if (hword == 209 || hword == 211) srcID += 3; #endif StringAppend(dst, gBattleStringsTable[hword - BATTLESTRING_TO_SUB]); srcID += 3; break; case B_BUFF_NUMBER: // int to string switch (src[srcID + 1]) { case 1: value = T1_READ_8(&src[srcID + 3]); break; case 2: value = T1_READ_16(&src[srcID + 3]); break; case 4: value = T1_READ_32(&src[srcID + 3]); break; } ConvertIntToDecimalStringN(dst, value, 0, src[srcID + 2]); srcID += src[srcID + 1] + 3; break; case B_BUFF_MOVE: // move name StringAppend(dst, gMoveNames[T1_READ_16(&src[srcID + 1])]); srcID += 3; break; case B_BUFF_TYPE: // type name StringAppend(dst, gTypeNames[src[srcID + 1]]); srcID += 2; break; case B_BUFF_MON_NICK_WITH_PREFIX: // poke nick with prefix #ifdef ENGLISH if (GetBattlerSide(src[srcID + 1]) == 0) { GetMonData(&gPlayerParty[src[srcID + 2]], MON_DATA_NICKNAME, text); } else { if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) StringAppend(dst, BattleText_Foe); else StringAppend(dst, BattleText_Wild); GetMonData(&gEnemyParty[src[srcID + 2]], MON_DATA_NICKNAME, text); } StringGetEnd10(text); StringAppend(dst, text); #else if (GetBattlerSide(src[srcID + 1]) == 0) { GetMonData(&gPlayerParty[src[srcID + 2]], MON_DATA_NICKNAME, text); StringGetEnd10(text); StringAppend(dst, text); } else { GetMonData(&gEnemyParty[src[srcID + 2]], MON_DATA_NICKNAME, text); StringGetEnd10(text); StringAppend(dst, text); if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) StringAppend(dst, BattleText_Foe); else StringAppend(dst, BattleText_Wild); } #endif srcID += 3; break; case B_BUFF_STAT: // stats StringAppend(dst, gUnknown_08400F58[src[srcID + 1]]); srcID += 2; break; case B_BUFF_SPECIES: // species name GetSpeciesName(dst, T1_READ_16(&src[srcID + 1])); srcID += 3; break; case B_BUFF_MON_NICK: // poke nick without prefix if (GetBattlerSide(src[srcID + 1]) == 0) GetMonData(&gPlayerParty[src[srcID + 2]], MON_DATA_NICKNAME, dst); else GetMonData(&gEnemyParty[src[srcID + 2]], MON_DATA_NICKNAME, dst); StringGetEnd10(dst); srcID += 3; break; case B_BUFF_NEGATIVE_FLAVOR: // flavour table StringAppend(dst, gUnknown_08400F78[src[srcID + 1]]); srcID += 2; break; case B_BUFF_ABILITY: // ability names StringAppend(dst, gAbilityNames[src[srcID + 1]]); srcID += 2; break; case B_BUFF_ITEM: // item name { hword = T1_READ_16(&src[srcID + 1]); if (gBattleTypeFlags & BATTLE_TYPE_LINK) { if (hword == ITEM_ENIGMA_BERRY) { if (gLinkPlayers[gBattleStruct->multiplayerId].id == gPotentialItemEffectBattler) { StringCopy(dst, gEnigmaBerries[gPotentialItemEffectBattler].name); #ifdef ENGLISH StringAppend(dst, BattleText_Berry); #else de_sub_8073174(dst, BattleText_Berry); #endif } else StringAppend(dst, BattleText_EnigmaBerry); } else CopyItemName(hword, dst); } else CopyItemName(hword, dst); srcID += 3; } break; } } } // Loads one of two text strings into the provided buffer. This is functionally // unused, since the value loaded into the buffer is not read; it loaded one of // two particles (either "は" or "の") which works in tandem with ChooseTypeOfMoveUsedString // below to effect changes in the meaning of the line. void ChooseMoveUsedParticle(u8* textBuff) { s32 counter = 0; u32 i = 0; while (counter != 4) { if (gUnknown_084016BC[i] == 0) counter++; if (gUnknown_084016BC[i++] == gStringInfo->currentMove) break; } if (counter >= 0) { if (counter <= 2) StringCopy(textBuff, BattleText_Format10); // is else if (counter <= 4) StringCopy(textBuff, BattleText_Format11); // 's } } // Appends "!" to the text buffer `dst`. In the original Japanese this looked // into the table of moves at gUnknown_084016BC and varied the line accordingly. // // BattleText_Exclamation was a plain "!", used for any attack not on the list. // It resulted in the translation "'s !". // // BattleText_Exclamation2 was "を つかった!". This resulted in the translation // " used !", which was used for all attacks in English. // // BattleText_Exclamation3 was "した!". This was used for those moves whose // names were verbs, such as Recover, and resulted in translations like " // recovered itself!". // // BattleText_Exclamation4 was "を した!" This resulted in a translation of // " did an !". // // BattleText_Exclamation5 was " こうげき!" This resulted in a translation of // "'s attack!". void ChooseTypeOfMoveUsedString(u8* dst) { s32 counter = 0; s32 i = 0; while (*dst != EOS) dst++; while (counter != 4) { if (gUnknown_084016BC[i] == 0) counter++; if (gUnknown_084016BC[i++] == gStringInfo->currentMove) break; } switch (counter) { case 0: StringCopy(dst, BattleText_Exclamation2); break; case 1: StringCopy(dst, BattleText_Exclamation3); break; case 2: StringCopy(dst, BattleText_Exclamation4); break; case 3: StringCopy(dst, BattleText_Exclamation5); break; case 4: StringCopy(dst, BattleText_Exclamation); break; } }