#include "global.h"
#include "rtc.h"
#include "overworld.h"
#include "random.h"
#include "event_data.h"
#include "fieldmap.h"
#include "field_camera.h"
#include "field_specials.h"
#include "fldeff.h"
#include "strings.h"
#include "string_util.h"
#include "international_string_util.h"
#include "pokemon_storage_system.h"
#include "field_message_box.h"
#include "easy_chat.h"
#include "battle.h"
#include "battle_tower.h"
#include "contest.h"
#include "item.h"
#include "link.h"
#include "main.h"
#include "event_scripts.h"
#include "shop.h"
#include "lilycove_lady.h"
#include "pokedex.h"
#include "event_object_movement.h"
#include "text.h"
#include "script_menu.h"
#include "naming_screen.h"
#include "malloc.h"
#include "region_map.h"
#include "decoration.h"
#include "secret_base.h"
#include "tv.h"
#include "pokeball.h"
#include "data.h"
#include "constants/battle_frontier.h"
#include "constants/contest.h"
#include "constants/decorations.h"
#include "constants/event_objects.h"
#include "constants/items.h"
#include "constants/layouts.h"
#include "constants/lilycove_lady.h"
#include "constants/metatile_behaviors.h"
#include "constants/metatile_labels.h"
#include "constants/moves.h"
#include "constants/region_map_sections.h"
#include "constants/script_menu.h"

#define LAST_TVSHOW_IDX (TV_SHOWS_COUNT - 1)

#define rbernoulli(num, den) BernoulliTrial(0xFFFF * (num) / (den))

enum {
    TVGROUP_NONE,
    TVGROUP_UNUSED,
    TVGROUP_NORMAL,
    TVGROUP_RECORD_MIX,
    TVGROUP_OUTBREAK,
};

enum {
    SLOT_MACHINE,
    ROULETTE,
};

s8 sCurTVShowSlot;
u16 sTV_SecretBaseVisitMovesTemp[8];
u8 sTV_DecorationsBuffer[DECOR_MAX_SECRET_BASE];
struct {
    u8 level;
    u16 species;
    u16 move;
} sTV_SecretBaseVisitMonsTemp[10];

static u8 sTVShowMixingNumPlayers;
static u8 sTVShowNewsMixingNumPlayers;
static s8 sTVShowMixingCurSlot;

static EWRAM_DATA u16 sPokemonAnglerSpecies = 0;
static EWRAM_DATA u16 sPokemonAnglerAttemptCounters = 0;
static EWRAM_DATA u16 sFindThatGamerCoinsSpent = 0;
static EWRAM_DATA u8 sFindThatGamerWhichGame = SLOT_MACHINE;
static EWRAM_DATA ALIGNED(4) u8 sRecordMixingPartnersWithoutShowsToShare = 0;
static EWRAM_DATA ALIGNED(4) u8 sTVShowState = 0;
static EWRAM_DATA u8 sTVSecretBaseSecretsRandomValues[3] = {};

static void ClearPokeNews(void);
static u8 GetTVGroupByShowId(u8);
static u8 FindFirstActiveTVShowThatIsNotAMassOutbreak(void);
static void SetTVMetatilesOnMap(int, int, u16);
static u8 FindAnyPokeNewsOnTheAir(void);
static void TakeGabbyAndTyOffTheAir(void);
static bool8 BernoulliTrial(u16 ratio);
static s8 FindFirstEmptyRecordMixTVShowSlot(TVShow *);
static bool8 IsRecordMixShowAlreadySpawned(u8, bool8);
static void StorePlayerIdInRecordMixShow(TVShow *);
static void DeleteTVShowInArrayByIdx(TVShow *, u8);
static s8 FindFirstEmptyNormalTVShowSlot(TVShow *);
static void TryReplaceOldTVShowOfKind(u8);
static void InterviewBefore_BravoTrainerPkmnProfile(void);
static void InterviewBefore_NameRater(void);
static u16 GetRandomDifferentSpeciesSeenByPlayer(u16);
static void Script_FindFirstEmptyNormalTVShowSlot(void);
static void CompactTVShowArray(TVShow *);
static s8 GetFirstEmptyPokeNewsSlot(PokeNews *);
static bool8 IsAddingPokeNewsDisallowed(u8);
static void ClearPokeNewsBySlot(u8);
static void TranslateRubyShows(TVShow *);
static void TranslateJapaneseEmeraldShows(TVShow *);
static void SetMixedTVShows(TVShow *, TVShow *, TVShow *, TVShow *);
static void DeleteExcessMixedShows(void);
static void DeactivateShowsWithUnseenSpecies(void);
static void DeactivateGameCompleteShowsIfNotUnlocked(void);
static s8 FindInactiveShowInArray(TVShow *);
static bool8 TryMixTVShow(TVShow *[], TVShow *[], u8);
static bool8 TryMixNormalTVShow(TVShow *, TVShow *, u8);
static bool8 TryMixRecordMixTVShow(TVShow *, TVShow *, u8);
static bool8 TryMixOutbreakTVShow(TVShow *, TVShow *, u8);
static void DeactivateShow(u8 showIdx);
static void DeactivateShowIfNotSeenSpecies(u16, u8);
static void SetMixedPokeNews(PokeNews *, PokeNews *, PokeNews *, PokeNews *);
static void ClearInvalidPokeNews(void);
static void ClearPokeNewsIfGameNotComplete(void);
static s8 GetPokeNewsSlotIfActive(PokeNews *, u8);
static void InitTryMixPokeNewsShow(PokeNews *[], PokeNews *[]);
static bool8 TryMixPokeNewsShow(PokeNews *, PokeNews *, s8);
static void TVShowDone(void);
static void InterviewAfter_FanClubLetter(void);
static void InterviewAfter_RecentHappenings(void);
static void InterviewAfter_PkmnFanClubOpinions(void);
static void InterviewAfter_Dummy(void);
static void InterviewAfter_BravoTrainerPokemonProfile(void);
static void InterviewAfter_BravoTrainerBattleTowerProfile(void);
static void InterviewAfter_ContestLiveUpdates(void);
static void InitWorldOfMastersShowAttempt(void);
static void TryPutPokemonTodayFailedOnTheAir(void);
static void TryStartRandomMassOutbreak(void);
static void TryPutRandomPokeNewsOnAir(void);
static void SortPurchasesByQuantity(void);
static void UpdateMassOutbreakTimeLeft(u16);
static void TryEndMassOutbreak(u16);
static void UpdatePokeNewsCountdown(u16);
static void ResolveWorldOfMastersShow(u16);
static void ResolveNumberOneShow(u16);
static void TryPutFishingAdviceOnAir(void);
static u8 MonDataIdxToRibbon(u8);
static void TryPutNumberOneOnAir(u8);
static bool8 ShouldApplyPokeNewsEffect(u8);
static void TryPutWorldOfMastersOnAir(void);
static void InterviewBefore_FanClubLetter(void);
static void InterviewBefore_RecentHappenings(void);
static void InterviewBefore_PkmnFanClubOpinions(void);
static void InterviewBefore_Dummy(void);
static void InterviewBefore_BravoTrainerBTProfile(void);
static void InterviewBefore_ContestLiveUpdates(void);
static void InterviewBefore_3CheersForPokeblocks(void);
static void InterviewBefore_FanClubSpecial(void);
static void ChangeBoxPokemonNickname_CB(void);
static void DoTVShowPokemonFanClubLetter(void);
static void DoTVShowRecentHappenings(void);
static void DoTVShowPokemonFanClubOpinions(void);
static void DoTVShowDummiedOut(void);
static void DoTVShowPokemonNewsMassOutbreak(void);
static void DoTVShowBravoTrainerPokemonProfile(void);
static void DoTVShowBravoTrainerBattleTower(void);
static void DoTVShowPokemonTodaySuccessfulCapture(void);
static void DoTVShowTodaysSmartShopper(void);
static void DoTVShowTheNameRaterShow(void);
static void DoTVShowPokemonContestLiveUpdates(void);
static void DoTVShowPokemonBattleUpdate(void);
static void DoTVShow3CheersForPokeblocks(void);
static void DoTVShowPokemonTodayFailedCapture(void);
static void DoTVShowPokemonAngler(void);
static void DoTVShowTheWorldOfMasters(void);
static void DoTVShowTodaysRivalTrainer(void);
static void DoTVShowDewfordTrendWatcherNetwork(void);
static void DoTVShowHoennTreasureInvestigators(void);
static void DoTVShowFindThatGamer(void);
static void DoTVShowBreakingNewsTV(void);
static void DoTVShowSecretBaseVisit(void);
static void DoTVShowPokemonLotteryWinnerFlashReport(void);
static void DoTVShowThePokemonBattleSeminar(void);
static void DoTVShowTrainerFanClubSpecial(void);
static void DoTVShowTrainerFanClub(void);
static void DoTVShowSpotTheCuties(void);
static void DoTVShowPokemonNewsBattleFrontier(void);
static void DoTVShowWhatsNo1InHoennToday(void);
static void DoTVShowSecretBaseSecrets(void);
static void DoTVShowSafariFanClub(void);
static void DoTVShowLilycoveContestLady(void);

static const struct {
    u16 species;
    u16 moves[MAX_MON_MOVES];
    u8 level;
    u8 location;
} sPokeOutbreakSpeciesList[] = {
    {
        .species = SPECIES_SEEDOT,
        .moves = {MOVE_BIDE, MOVE_HARDEN, MOVE_LEECH_SEED},
        .level = 3,
        .location = MAP_NUM(ROUTE102)
    },
    {
        .species = SPECIES_NUZLEAF,
        .moves = {MOVE_HARDEN, MOVE_GROWTH, MOVE_NATURE_POWER, MOVE_LEECH_SEED},
        .level = 15,
        .location = MAP_NUM(ROUTE114),
    },
    {
        .species = SPECIES_SEEDOT,
        .moves = {MOVE_HARDEN, MOVE_GROWTH, MOVE_NATURE_POWER, MOVE_LEECH_SEED},
        .level = 13,
        .location = MAP_NUM(ROUTE117),
    },
    {
        .species = SPECIES_SEEDOT,
        .moves = {MOVE_GIGA_DRAIN, MOVE_FRUSTRATION, MOVE_SOLAR_BEAM, MOVE_LEECH_SEED},
        .level = 25,
        .location = MAP_NUM(ROUTE120),
    },
    {
        .species = SPECIES_SKITTY,
        .moves = {MOVE_GROWL, MOVE_TACKLE, MOVE_TAIL_WHIP, MOVE_ATTRACT},
        .level = 8,
        .location = MAP_NUM(ROUTE116),
    }
};

static const u16 sGoldSymbolFlags[NUM_FRONTIER_FACILITIES] = {
    [FRONTIER_FACILITY_TOWER]   = FLAG_SYS_TOWER_GOLD,
    [FRONTIER_FACILITY_DOME]    = FLAG_SYS_DOME_GOLD,
    [FRONTIER_FACILITY_PALACE]  = FLAG_SYS_PALACE_GOLD,
    [FRONTIER_FACILITY_ARENA]   = FLAG_SYS_ARENA_GOLD,
    [FRONTIER_FACILITY_FACTORY] = FLAG_SYS_FACTORY_GOLD,
    [FRONTIER_FACILITY_PIKE]    = FLAG_SYS_PIKE_GOLD,
    [FRONTIER_FACILITY_PYRAMID] = FLAG_SYS_PYRAMID_GOLD
};

static const u16 sSilverSymbolFlags[NUM_FRONTIER_FACILITIES] = {
    [FRONTIER_FACILITY_TOWER]   = FLAG_SYS_TOWER_SILVER,
    [FRONTIER_FACILITY_DOME]    = FLAG_SYS_DOME_SILVER,
    [FRONTIER_FACILITY_PALACE]  = FLAG_SYS_PALACE_SILVER,
    [FRONTIER_FACILITY_ARENA]   = FLAG_SYS_ARENA_SILVER,
    [FRONTIER_FACILITY_FACTORY] = FLAG_SYS_FACTORY_SILVER,
    [FRONTIER_FACILITY_PIKE]    = FLAG_SYS_PIKE_SILVER,
    [FRONTIER_FACILITY_PYRAMID] = FLAG_SYS_PYRAMID_SILVER
};

static const u16 sNumberOneVarsAndThresholds[][2] = {
    {VAR_DAILY_SLOTS, 100},
    {VAR_DAILY_ROULETTE,  50},
    {VAR_DAILY_WILDS, 100},
    {VAR_DAILY_BLENDER,  20},
    {VAR_DAILY_PLANTED_BERRIES,  20},
    {VAR_DAILY_PICKED_BERRIES,  20},
    {VAR_DAILY_BP,  30}
};

static const u8 *const sPokeNewsTextGroup_Upcoming[NUM_POKENEWS_TYPES + 1] = {
    [POKENEWS_NONE]        = NULL,
    [POKENEWS_SLATEPORT]   = gPokeNewsTextSlateport_Upcoming,
    [POKENEWS_GAME_CORNER] = gPokeNewsTextGameCorner_Upcoming,
    [POKENEWS_LILYCOVE]    = gPokeNewsTextLilycove_Upcoming,
    [POKENEWS_BLENDMASTER] = gPokeNewsTextBlendMaster_Upcoming
};

static const u8 *const sPokeNewsTextGroup_Ongoing[NUM_POKENEWS_TYPES + 1] = {
    [POKENEWS_NONE]        = NULL,
    [POKENEWS_SLATEPORT]   = gPokeNewsTextSlateport_Ongoing,
    [POKENEWS_GAME_CORNER] = gPokeNewsTextGameCorner_Ongoing,
    [POKENEWS_LILYCOVE]    = gPokeNewsTextLilycove_Ongoing,
    [POKENEWS_BLENDMASTER] = gPokeNewsTextBlendMaster_Ongoing
};

static const u8 *const sPokeNewsTextGroup_Ending[NUM_POKENEWS_TYPES + 1] = {
    [POKENEWS_NONE]        = NULL,
    [POKENEWS_SLATEPORT]   = gPokeNewsTextSlateport_Ending,
    [POKENEWS_GAME_CORNER] = gPokeNewsTextGameCorner_Ending,
    [POKENEWS_LILYCOVE]    = gPokeNewsTextLilycove_Ending,
    [POKENEWS_BLENDMASTER] = gPokeNewsTextBlendMaster_Ending
};

u8 *const gTVStringVarPtrs[] = {
    gStringVar1,
    gStringVar2,
    gStringVar3
};

static const u8 *const sTVFanClubTextGroup[] = {
    gTVFanClubText00,
    gTVFanClubText01,
    gTVFanClubText02,
    gTVFanClubText03,
    gTVFanClubText04,
    gTVFanClubText05,
    gTVFanClubText06,
    gTVFanClubText07
};

static const u8 *const sTVRecentHappeninssTextGroup[] = {
    gTVRecentHappeningsText00,
    gTVRecentHappeningsText01,
    gTVRecentHappeningsText02,
    gTVRecentHappeningsText03,
    gTVRecentHappeningsText04,
    gTVRecentHappeningsText05
};

static const u8 *const sTVFanClubOpinionsTextGroup[] = {
    gTVFanClubOpinionsText00,
    gTVFanClubOpinionsText01,
    gTVFanClubOpinionsText02,
    gTVFanClubOpinionsText03,
    gTVFanClubOpinionsText04
};

static const u8 *const sTVMassOutbreakTextGroup[] = {
    gTVMassOutbreakText00
};

static const u8 *const sTVPokemonTodaySuccessfulTextGroup[] = {
    gTVPokemonTodaySuccessfulText00,
    gTVPokemonTodaySuccessfulText01,
    gTVPokemonTodaySuccessfulText02,
    gTVPokemonTodaySuccessfulText03,
    gTVPokemonTodaySuccessfulText04,
    gTVPokemonTodaySuccessfulText05,
    gTVPokemonTodaySuccessfulText06,
    gTVPokemonTodaySuccessfulText07,
    gTVPokemonTodaySuccessfulText08,
    gTVPokemonTodaySuccessfulText09,
    gTVPokemonTodaySuccessfulText10,
    gTVPokemonTodaySuccessfulText11
};

static const u8 *const sTVTodaysSmartShopperTextGroup[] = {
    [SMARTSHOPPER_STATE_INTRO]          = SmartShopper_Text_Intro,
    [SMARTSHOPPER_STATE_CLERK_NORMAL]   = SmartShopper_Text_ClerkNormal,
    [SMARTSHOPPER_STATE_RAND_COMMENT_1] = SmartShopper_Text_RandomComment1,
    [SMARTSHOPPER_STATE_RAND_COMMENT_2] = SmartShopper_Text_RandomComment2,
    [SMARTSHOPPER_STATE_RAND_COMMENT_3] = SmartShopper_Text_RandomComment3,
    [SMARTSHOPPER_STATE_RAND_COMMENT_4] = SmartShopper_Text_RandomComment4,
    [SMARTSHOPPER_STATE_SECOND_ITEM]    = SmartShopper_Text_SecondItem,
    [SMARTSHOPPER_STATE_THIRD_ITEM]     = SmartShopper_Text_ThirdItem,
    [SMARTSHOPPER_STATE_DURING_SALE]    = SmartShopper_Text_DuringSale,
    [SMARTSHOPPER_STATE_OUTRO_NORMAL]   = SmartShopper_Text_OutroNormal,
    [SMARTSHOPPER_STATE_IS_VIP]         = SmartShopper_Text_IsVIP,
    [SMARTSHOPPER_STATE_CLERK_MAX]      = SmartShopper_Text_ClerkMax,
    [SMARTSHOPPER_STATE_OUTRO_MAX]      = SmartShopper_Text_OutroMax
};

static const u8 *const sTVBravoTrainerTextGroup[] = {
    gTVBravoTrainerText00,
    gTVBravoTrainerText01,
    gTVBravoTrainerText02,
    gTVBravoTrainerText03,
    gTVBravoTrainerText04,
    gTVBravoTrainerText05,
    gTVBravoTrainerText06,
    gTVBravoTrainerText07,
    gTVBravoTrainerText08
};

static const u8 *const sTV3CheersForPokeblocksTextGroup[] = {
    gTV3CheersForPokeblocksText00,
    gTV3CheersForPokeblocksText01,
    gTV3CheersForPokeblocksText02,
    gTV3CheersForPokeblocksText03,
    gTV3CheersForPokeblocksText04,
    gTV3CheersForPokeblocksText05
};

static const u8 *const sTVBravoTrainerBattleTowerTextGroup[] = {
    gTVBravoTrainerBattleTowerText00,
    gTVBravoTrainerBattleTowerText01,
    gTVBravoTrainerBattleTowerText02,
    gTVBravoTrainerBattleTowerText03,
    gTVBravoTrainerBattleTowerText04,
    gTVBravoTrainerBattleTowerText05,
    gTVBravoTrainerBattleTowerText06,
    gTVBravoTrainerBattleTowerText07,
    gTVBravoTrainerBattleTowerText08,
    gTVBravoTrainerBattleTowerText09,
    gTVBravoTrainerBattleTowerText10,
    gTVBravoTrainerBattleTowerText11,
    gTVBravoTrainerBattleTowerText12,
    gTVBravoTrainerBattleTowerText13,
    gTVBravoTrainerBattleTowerText14
};

static const u8 *const sTVContestLiveUpdatesTextGroup[] = {
    [CONTESTLIVE_STATE_INTRO]                 = ContestLiveUpdates_Text_Intro,
    [CONTESTLIVE_STATE_WON_BOTH_ROUNDS]       = ContestLiveUpdates_Text_WonBothRounds,
    [CONTESTLIVE_STATE_BETTER_ROUND2]         = ContestLiveUpdates_Text_BetterRound2,
    [CONTESTLIVE_STATE_EQUAL_ROUNDS]          = ContestLiveUpdates_Text_EqualRounds,
    [CONTESTLIVE_STATE_BETTER_ROUND1]         = ContestLiveUpdates_Text_BetterRound1,
    [CONTESTLIVE_STATE_GOT_NERVOUS]           = ContestLiveUpdates_Text_GotNervous,
    [CONTESTLIVE_STATE_STARTLED_OTHER]        = ContestLiveUpdates_Text_StartledFoes,
    [CONTESTLIVE_STATE_USED_COMBO]            = ContestLiveUpdates_Text_UsedCombo,
    [CONTESTLIVE_STATE_EXCITING_APPEAL]       = ContestLiveUpdates_Text_ExcitingAppeal,
    [CONTESTLIVE_STATE_COOL]                  = ContestLiveUpdates_Text_WasCool,
    [CONTESTLIVE_STATE_BEAUTIFUL]             = ContestLiveUpdates_Text_WasBeautiful,
    [CONTESTLIVE_STATE_CUTE]                  = ContestLiveUpdates_Text_WasCute,
    [CONTESTLIVE_STATE_SMART]                 = ContestLiveUpdates_Text_WasSmart,
    [CONTESTLIVE_STATE_TOUGH]                 = ContestLiveUpdates_Text_WasTough,
    [CONTESTLIVE_STATE_VERY_EXCITING_APPEAL]  = ContestLiveUpdates_Text_VeryExcitingAppeal,
    [CONTESTLIVE_STATE_VERY_COOL]             = ContestLiveUpdates_Text_VeryCool,
    [CONTESTLIVE_STATE_VERY_BEAUTIFUL]        = ContestLiveUpdates_Text_VeryBeautiful,
    [CONTESTLIVE_STATE_VERY_CUTE]             = ContestLiveUpdates_Text_VeryCute,
    [CONTESTLIVE_STATE_VERY_SMART]            = ContestLiveUpdates_Text_VerySmart,
    [CONTESTLIVE_STATE_VERY_TOUGH]            = ContestLiveUpdates_Text_VeryTough,
    [CONTESTLIVE_STATE_TOOK_BREAK]            = ContestLiveUpdates_Text_TookBreak,
    [CONTESTLIVE_STATE_GOT_STARTLED]          = ContestLiveUpdates_Text_GotStartled,
    [CONTESTLIVE_STATE_USED_MOVE]             = ContestLiveUpdates_Text_MoveWonderful,
    [CONTESTLIVE_STATE_TALK_ABOUT_LOSER]      = ContestLiveUpdates_Text_TalkAboutAnotherMon,
    [CONTESTLIVE_STATE_NO_APPEALS]            = ContestLiveUpdates_Text_FailedToAppeal,
    [CONTESTLIVE_STATE_LAST_BOTH]             = ContestLiveUpdates_Text_LastInBothRounds,
    [CONTESTLIVE_STATE_NOT_EXCITING_ENOUGH]   = ContestLiveUpdates_Text_NotExcitingEnough,
    [CONTESTLIVE_STATE_LOST_AFTER_ROUND1_WIN] = ContestLiveUpdates_Text_LostAfterWinningRound1,
    [CONTESTLIVE_STATE_NO_EXCITING_APPEALS]   = ContestLiveUpdates_Text_NeverExciting,
    [CONTESTLIVE_STATE_LOST_SMALL_MARGIN]     = ContestLiveUpdates_Text_LostBySmallMargin,
    [CONTESTLIVE_STATE_REPEATED_APPEALS]      = ContestLiveUpdates_Text_RepeatedAppeals,
    [CONTESTLIVE_STATE_LOST]                  = ContestLiveUpdates_Text_ValiantEffortButLost,
    [CONTESTLIVE_STATE_OUTRO]                 = ContestLiveUpdates_Text_Outro
};

static const u8 *const sTVPokemonBattleUpdateTextGroup[] = {
    gTVPokemonBattleUpdateText00,
    gTVPokemonBattleUpdateText01,
    gTVPokemonBattleUpdateText02,
    gTVPokemonBattleUpdateText03,
    gTVPokemonBattleUpdateText04,
    gTVPokemonBattleUpdateText05,
    gTVPokemonBattleUpdateText06,
    gTVPokemonBattleUpdateText07
};

static const u8 *const sTVTrainerFanClubSpecialTextGroup[] = {
    gTVTrainerFanClubSpecialText00,
    gTVTrainerFanClubSpecialText01,
    gTVTrainerFanClubSpecialText02,
    gTVTrainerFanClubSpecialText03,
    gTVTrainerFanClubSpecialText04,
    gTVTrainerFanClubSpecialText05
};

static const u8 *const sTVNameRaterTextGroup[] = {
    gTVNameRaterText00,
    gTVNameRaterText01,
    gTVNameRaterText02,
    gTVNameRaterText03,
    gTVNameRaterText04,
    gTVNameRaterText05,
    gTVNameRaterText06,
    gTVNameRaterText07,
    gTVNameRaterText08,
    gTVNameRaterText09,
    gTVNameRaterText10,
    gTVNameRaterText11,
    gTVNameRaterText12,
    gTVNameRaterText13,
    gTVNameRaterText14,
    gTVNameRaterText15,
    gTVNameRaterText16,
    gTVNameRaterText17,
    gTVNameRaterText18
};

static const u8 *const sTVLilycoveContestLadyTextGroup[] = {
    [CONTESTLADYLIVE_STATE_INTRO]      = ContestLadyShow_Text_Intro,
    [CONTESTLADYLIVE_STATE_WON]        = ContestLadyShow_Text_Won,
    [CONTESTLADYLIVE_STATE_LOST]       = ContestLadyShow_Text_Lost,
    [CONTESTLADYLIVE_STATE_LOST_BADLY] = ContestLadyShow_Text_LostBadly
};

static const u8 *const sTVPokemonTodayFailedTextGroup[] = {
    gTVPokemonTodayFailedText00,
    gTVPokemonTodayFailedText01,
    gTVPokemonTodayFailedText02,
    gTVPokemonTodayFailedText03,
    gTVPokemonTodayFailedText04,
    gTVPokemonTodayFailedText05,
    gTVPokemonTodayFailedText06
};

static const u8 *const sTVPokemonAnslerTextGroup[] = {
    gTVPokemonAnglerText00,
    gTVPokemonAnglerText01
};

static const u8 *const sTVWorldOfMastersTextGroup[] = {
    gTVWorldOfMastersText00,
    gTVWorldOfMastersText01,
    gTVWorldOfMastersText02
};

static const u8 *const sTVTodaysRivalTrainerTextGroup[] = {
    gTVTodaysRivalTrainerText00,
    gTVTodaysRivalTrainerText01,
    gTVTodaysRivalTrainerText02,
    gTVTodaysRivalTrainerText03,
    gTVTodaysRivalTrainerText04,
    gTVTodaysRivalTrainerText05,
    gTVTodaysRivalTrainerText06,
    gTVTodaysRivalTrainerText07,
    gTVTodaysRivalTrainerText08,
    gTVTodaysRivalTrainerText09,
    gTVTodaysRivalTrainerText10
};

static const u8 *const sTVDewfordTrendWatcherNetworkTextGroup[] = {
    [TRENDWATCHER_STATE_INTRO]           = TrendWatcher_Text_Intro,
    [TRENDWATCHER_STATE_TAUGHT_MALE]     = TrendWatcher_Text_MaleTaughtMePhrase,
    [TRENDWATCHER_STATE_TAUGHT_FEMALE]   = TrendWatcher_Text_FemaleTaughtMePhrase,
    [TRENDWATCHER_STATE_PHRASE_HOPELESS] = TrendWatcher_Text_PhraseWasHopeless,
    [TRENDWATCHER_STATE_BIGGER_MALE]     = TrendWatcher_Text_MaleTellMeBigger,
    [TRENDWATCHER_STATE_BIGGER_FEMALE]   = TrendWatcher_Text_FemaleTellMeBigger,
    [TRENDWATCHER_STATE_OUTRO]           = TrendWatcher_Text_Outro
};

static const u8 *const sTVHoennTreasureInvestisatorsTextGroup[] = {
    gTVHoennTreasureInvestigatorsText00,
    gTVHoennTreasureInvestigatorsText01,
    gTVHoennTreasureInvestigatorsText02
};

static const u8 *const sTVFindThatGamerTextGroup[] = {
    gTVFindThatGamerText00,
    gTVFindThatGamerText01,
    gTVFindThatGamerText02,
    gTVFindThatGamerText03
};

static const u8 *const sTVBreakingNewsTextGroup[] = {
    gTVBreakingNewsText00,
    gTVBreakingNewsText01,
    gTVBreakingNewsText02,
    gTVBreakingNewsText03,
    gTVBreakingNewsText04,
    gTVBreakingNewsText05,
    gTVBreakingNewsText06,
    gTVBreakingNewsText07,
    gTVBreakingNewsText08,
    gTVBreakingNewsText09,
    gTVBreakingNewsText10,
    gTVBreakingNewsText11,
    gTVBreakingNewsText12
};

static const u8 *const sTVSecretBaseVisitTextGroup[] = {
    gTVSecretBaseVisitText00,
    gTVSecretBaseVisitText01,
    gTVSecretBaseVisitText02,
    gTVSecretBaseVisitText03,
    gTVSecretBaseVisitText04,
    gTVSecretBaseVisitText05,
    gTVSecretBaseVisitText06,
    gTVSecretBaseVisitText07,
    gTVSecretBaseVisitText08,
    gTVSecretBaseVisitText09,
    gTVSecretBaseVisitText10,
    gTVSecretBaseVisitText11,
    gTVSecretBaseVisitText12,
    gTVSecretBaseVisitText13
};

static const u8 *const sTVPokemonLotteryWinnerFlashReportTextGroup[] = {
    gTVPokemonLotteryWinnerFlashReportText00
};

static const u8 *const sTVThePokemonBattleSeminarTextGroup[] = {
    gTVThePokemonBattleSeminarText00,
    gTVThePokemonBattleSeminarText01,
    gTVThePokemonBattleSeminarText02,
    gTVThePokemonBattleSeminarText03,
    gTVThePokemonBattleSeminarText04,
    gTVThePokemonBattleSeminarText05,
    gTVThePokemonBattleSeminarText06
};

static const u8 *const sTVTrainerFanClubTextGroup[] = {
    gTVTrainerFanClubText00,
    gTVTrainerFanClubText01,
    gTVTrainerFanClubText02,
    gTVTrainerFanClubText03,
    gTVTrainerFanClubText04,
    gTVTrainerFanClubText05,
    gTVTrainerFanClubText06,
    gTVTrainerFanClubText07,
    gTVTrainerFanClubText08,
    gTVTrainerFanClubText09,
    gTVTrainerFanClubText10,
    gTVTrainerFanClubText11
};

static const u8 *const sTVCutiesTextGroup[] = {
    [SPOTCUTIES_STATE_INTRO]           = TVSpotTheCuties_Text_Intro,
    [SPOTCUTIES_STATE_RIBBONS_LOW]     = TVSpotTheCuties_Text_RibbonsLow,
    [SPOTCUTIES_STATE_RIBBONS_MID]     = TVSpotTheCuties_Text_RibbonsMid,
    [SPOTCUTIES_STATE_RIBBONS_HIGH]    = TVSpotTheCuties_Text_RibbonsHigh,
    [SPOTCUTIES_STATE_RIBBON_INTRO]    = TVSpotTheCuties_Text_RibbonIntro,
    [SPOTCUTIES_STATE_RIBBON_CHAMPION] = TVSpotTheCuties_Text_RibbonChampion,
    [SPOTCUTIES_STATE_RIBBON_COOL]     = TVSpotTheCuties_Text_RibbonCool,
    [SPOTCUTIES_STATE_RIBBON_BEAUTY]   = TVSpotTheCuties_Text_RibbonBeauty,
    [SPOTCUTIES_STATE_RIBBON_CUTE]     = TVSpotTheCuties_Text_RibbonCute,
    [SPOTCUTIES_STATE_RIBBON_SMART]    = TVSpotTheCuties_Text_RibbonSmart,
    [SPOTCUTIES_STATE_RIBBON_TOUGH]    = TVSpotTheCuties_Text_RibbonTough,
    [SPOTCUTIES_STATE_RIBBON_WINNING]  = TVSpotTheCuties_Text_RibbonWinning,
    [SPOTCUTIES_STATE_RIBBON_VICTORY]  = TVSpotTheCuties_Text_RibbonVictory,
    [SPOTCUTIES_STATE_RIBBON_ARTIST]   = TVSpotTheCuties_Text_RibbonArtist,
    [SPOTCUTIES_STATE_RIBBON_EFFORT]   = TVSpotTheCuties_Text_RibbonEffort,
    [SPOTCUTIES_STATE_OUTRO]           = TVSpotTheCuties_Text_Outro
};

static const u8 *const sTVPokemonNewsBattleFrontierTextGroup[] = {
    gTVPokemonNewsBattleFrontierText00,
    gTVPokemonNewsBattleFrontierText01,
    gTVPokemonNewsBattleFrontierText02,
    gTVPokemonNewsBattleFrontierText03,
    gTVPokemonNewsBattleFrontierText04,
    gTVPokemonNewsBattleFrontierText05,
    gTVPokemonNewsBattleFrontierText06,
    gTVPokemonNewsBattleFrontierText07,
    gTVPokemonNewsBattleFrontierText08,
    gTVPokemonNewsBattleFrontierText09,
    gTVPokemonNewsBattleFrontierText10,
    gTVPokemonNewsBattleFrontierText11,
    gTVPokemonNewsBattleFrontierText12,
    gTVPokemonNewsBattleFrontierText13,
    gTVPokemonNewsBattleFrontierText14,
    gTVPokemonNewsBattleFrontierText15,
    gTVPokemonNewsBattleFrontierText16,
    gTVPokemonNewsBattleFrontierText17,
    gTVPokemonNewsBattleFrontierText18
};

static const u8 *const sTVWhatsNo1InHoennTodayTextGroup[] = {
    gTVWhatsNo1InHoennTodayText00,
    gTVWhatsNo1InHoennTodayText01,
    gTVWhatsNo1InHoennTodayText02,
    gTVWhatsNo1InHoennTodayText03,
    gTVWhatsNo1InHoennTodayText04,
    gTVWhatsNo1InHoennTodayText05,
    gTVWhatsNo1InHoennTodayText06,
    gTVWhatsNo1InHoennTodayText07,
    gTVWhatsNo1InHoennTodayText08
};

static const u8 *const sTVSecretBaseSecretsTextGroup[SBSECRETS_NUM_STATES] =
{
    [SBSECRETS_STATE_INTRO]               = TVSecretBaseSecrets_Text_Intro,
    [SBSECRETS_STATE_DO_NEXT1]            = TVSecretBaseSecrets_Text_WhatWillPlayerDoNext1,
    [SBSECRETS_STATE_DO_NEXT2]            = TVSecretBaseSecrets_Text_WhatWillPlayerDoNext2,
    [SBSECRETS_STATE_TOOK_X_STEPS]        = TVSecretBaseSecrets_Text_TookXStepsBeforeLeaving,
    [SBSECRETS_STATE_BASE_INTEREST_LOW]   = TVSecretBaseSecrets_Text_BaseFailedToInterestPlayer,
    [SBSECRETS_STATE_BASE_INTEREST_MED]   = TVSecretBaseSecrets_Text_PlayerEnjoyedBase,
    [SBSECRETS_STATE_BASE_INTEREST_HIGH]  = TVSecretBaseSecrets_Text_PlayerHugeFanOfBase,
    [SBSECRETS_STATE_OUTRO]               = TVSecretBaseSecrets_Text_Outro,
    [SBSECRETS_STATE_NOTHING_USED1]       = TVSecretBaseSecrets_Text_StoppedMoving1,
    [SBSECRETS_STATE_NOTHING_USED2]       = TVSecretBaseSecrets_Text_StoppedMoving2,
    [SBSECRETS_STATE_USED_CHAIR]          = TVSecretBaseSecrets_Text_UsedChair,
    [SBSECRETS_STATE_USED_BALLOON]        = TVSecretBaseSecrets_Text_UsedBalloon,
    [SBSECRETS_STATE_USED_TENT]           = TVSecretBaseSecrets_Text_UsedTent,
    [SBSECRETS_STATE_USED_PLANT]          = TVSecretBaseSecrets_Text_UsedPlant,
    [SBSECRETS_STATE_USED_GOLD_SHIELD]    = TVSecretBaseSecrets_Text_UsedGoldShield,
    [SBSECRETS_STATE_USED_SILVER_SHIELD]  = TVSecretBaseSecrets_Text_UsedSilverShield,
    [SBSECRETS_STATE_USED_GLASS_ORNAMENT] = TVSecretBaseSecrets_Text_UsedGlassOrnament,
    [SBSECRETS_STATE_USED_TV]             = TVSecretBaseSecrets_Text_UsedTV,
    [SBSECRETS_STATE_USED_MUD_BALL]       = TVSecretBaseSecrets_Text_UsedMudBall,
    [SBSECRETS_STATE_USED_BAG]            = TVSecretBaseSecrets_Text_UsedBag,
    [SBSECRETS_STATE_USED_CUSHION]        = TVSecretBaseSecrets_Text_UsedCushion,
    [SBSECRETS_STATE_HIT_CUSHION]         = TVSecretBaseSecrets_Text_HitCushion,
    [SBSECRETS_STATE_HUGGED_CUSHION]      = TVSecretBaseSecrets_Text_HuggedCushion,
    [SBSECRETS_STATE_BATTLED_WON]         = TVSecretBaseSecrets_Text_BattledWon,
    [SBSECRETS_STATE_BATTLED_LOST]        = TVSecretBaseSecrets_Text_BattledLost,
    [SBSECRETS_STATE_DECLINED_BATTLE]     = TVSecretBaseSecrets_Text_DeclinedBattle,
    [SBSECRETS_STATE_USED_POSTER]         = TVSecretBaseSecrets_Text_UsedPoster,
    [SBSECRETS_STATE_USED_NOTE_MAT]       = TVSecretBaseSecrets_Text_UsedNoteMat,
    [SBSECRETS_STATE_BATTLED_DRAW]        = TVSecretBaseSecrets_Text_BattledDraw,
    [SBSECRETS_STATE_USED_SPIN_MAT]       = TVSecretBaseSecrets_Text_UsedSpinMat,
    [SBSECRETS_STATE_USED_SAND_ORNAMENT]  = TVSecretBaseSecrets_Text_UsedSandOrnament,
    [SBSECRETS_STATE_USED_DESK]           = TVSecretBaseSecrets_Text_UsedDesk,
    [SBSECRETS_STATE_USED_BRICK]          = TVSecretBaseSecrets_Text_UsedBrick,
    [SBSECRETS_STATE_USED_SOLID_BOARD]    = TVSecretBaseSecrets_Text_UsedSolidBoard,
    [SBSECRETS_STATE_USED_FENCE]          = TVSecretBaseSecrets_Text_UsedFence,
    [SBSECRETS_STATE_USED_GLITTER_MAT]    = TVSecretBaseSecrets_Text_UsedGlitterMat,
    [SBSECRETS_STATE_USED_TIRE]           = TVSecretBaseSecrets_Text_UsedTire,
    [SBSECRETS_STATE_USED_STAND]          = TVSecretBaseSecrets_Text_UsedStand,
    [SBSECRETS_STATE_USED_BREAKABLE_DOOR] = TVSecretBaseSecrets_Text_BrokeDoor,
    [SBSECRETS_STATE_USED_DOLL]           = TVSecretBaseSecrets_Text_UsedDoll,
    [SBSECRETS_STATE_USED_SLIDE]          = TVSecretBaseSecrets_Text_UsedSlide,
    [SBSECRETS_STATE_DECLINED_SLIDE]      = TVSecretBaseSecrets_Text_UsedSlideButDidntGoDown,
    [SBSECRETS_STATE_USED_JUMP_MAT]       = TVSecretBaseSecrets_Text_UsedJumpMat
};

static const u8 *const sTVSafariFanClubTextGroup[] = {
    gTVSafariFanClubText00,
    gTVSafariFanClubText01,
    gTVSafariFanClubText02,
    gTVSafariFanClubText03,
    gTVSafariFanClubText04,
    gTVSafariFanClubText05,
    gTVSafariFanClubText06,
    gTVSafariFanClubText07,
    gTVSafariFanClubText08,
    gTVSafariFanClubText09,
    gTVSafariFanClubText10
};

static const u8 *const sTVInSearchOfTrainersTextGroup[] = {
    gTVInSearchOfTrainersText00,
    gTVInSearchOfTrainersText01,
    gTVInSearchOfTrainersText02,
    gTVInSearchOfTrainersText03,
    gTVInSearchOfTrainersText04,
    gTVInSearchOfTrainersText05,
    gTVInSearchOfTrainersText06,
    gTVInSearchOfTrainersText07,
    gTVInSearchOfTrainersText08
};

// Secret Base Secrets TV Show states for actions that can be taken in a secret base
// The flags that determine whether or not the action was taken are commented
const u8 sTVSecretBaseSecretsActions[NUM_SECRET_BASE_FLAGS] =
{
    SBSECRETS_STATE_USED_CHAIR,             // SECRET_BASE_USED_CHAIR
    SBSECRETS_STATE_USED_BALLOON,           // SECRET_BASE_USED_BALLOON
    SBSECRETS_STATE_USED_TENT,              // SECRET_BASE_USED_TENT
    SBSECRETS_STATE_USED_PLANT,             // SECRET_BASE_USED_PLANT
    SBSECRETS_STATE_USED_GOLD_SHIELD,       // SECRET_BASE_USED_GOLD_SHIELD
    SBSECRETS_STATE_USED_SILVER_SHIELD,     // SECRET_BASE_USED_SILVER_SHIELD
    SBSECRETS_STATE_USED_GLASS_ORNAMENT,    // SECRET_BASE_USED_GLASS_ORNAMENT
    SBSECRETS_STATE_USED_TV,                // SECRET_BASE_USED_TV
    SBSECRETS_STATE_USED_MUD_BALL,          // SECRET_BASE_USED_MUD_BALL
    SBSECRETS_STATE_USED_BAG,               // SECRET_BASE_USED_BAG
    SBSECRETS_STATE_USED_CUSHION,           // SECRET_BASE_USED_CUSHION
    SBSECRETS_STATE_BATTLED_WON,            // SECRET_BASE_BATTLED_WON
    SBSECRETS_STATE_BATTLED_LOST,           // SECRET_BASE_BATTLED_LOST
    SBSECRETS_STATE_DECLINED_BATTLE,        // SECRET_BASE_DECLINED_BATTLE
    SBSECRETS_STATE_USED_POSTER,            // SECRET_BASE_USED_POSTER
    SBSECRETS_STATE_USED_NOTE_MAT,          // SECRET_BASE_USED_NOTE_MAT
    SBSECRETS_STATE_BATTLED_DRAW,           // SECRET_BASE_BATTLED_DRAW
    SBSECRETS_STATE_USED_SPIN_MAT,          // SECRET_BASE_USED_SPIN_MAT
    SBSECRETS_STATE_USED_SAND_ORNAMENT,     // SECRET_BASE_USED_SAND_ORNAMENT
    SBSECRETS_STATE_USED_DESK,              // SECRET_BASE_USED_DESK
    SBSECRETS_STATE_USED_BRICK,             // SECRET_BASE_USED_BRICK
    SBSECRETS_STATE_USED_SOLID_BOARD,       // SECRET_BASE_USED_SOLID_BOARD
    SBSECRETS_STATE_USED_FENCE,             // SECRET_BASE_USED_FENCE
    SBSECRETS_STATE_USED_GLITTER_MAT,       // SECRET_BASE_USED_GLITTER_MAT
    SBSECRETS_STATE_USED_TIRE,              // SECRET_BASE_USED_TIRE
    SBSECRETS_STATE_USED_STAND,             // SECRET_BASE_USED_STAND
    SBSECRETS_STATE_USED_BREAKABLE_DOOR,    // SECRET_BASE_USED_BREAKABLE_DOOR
    SBSECRETS_STATE_USED_DOLL,              // SECRET_BASE_USED_DOLL
    SBSECRETS_STATE_USED_SLIDE,             // SECRET_BASE_USED_SLIDE
    SBSECRETS_STATE_DECLINED_SLIDE,         // SECRET_BASE_DECLINED_SLIDE
    SBSECRETS_STATE_USED_JUMP_MAT,          // SECRET_BASE_USED_JUMP_MAT
    SBSECRETS_NUM_STATES                    // SECRET_BASE_UNUSED_FLAG. Odd that this is included, if it were used it would overflow sTVSecretBaseSecretsTextGroup
};

void ClearTVShowData(void)
{
    u8 i, j;

    for (i = 0; i < ARRAY_COUNT(gSaveBlock1Ptr->tvShows); i++)
    {
        gSaveBlock1Ptr->tvShows[i].commonInit.kind = 0;
        gSaveBlock1Ptr->tvShows[i].commonInit.active = 0;
        for (j = 0; j < ARRAY_COUNT(gSaveBlock1Ptr->tvShows[i].commonInit.data); j++)
            gSaveBlock1Ptr->tvShows[i].commonInit.data[j] = 0;
    }
    ClearPokeNews();
}

u8 GetRandomActiveShowIdx(void)
{
    u8 i;
    u8 j;
    u8 selIdx;
    TVShow *show;

    // Include all normal TV shows, and up through any present Record Mix shows
    for (i = NUM_NORMAL_TVSHOW_SLOTS; i < LAST_TVSHOW_IDX; i++)
    {
        if (gSaveBlock1Ptr->tvShows[i].common.kind == TVSHOW_OFF_AIR)
            break;
    }
    j = Random() % i;
    selIdx = j;
    do
    {
        if (GetTVGroupByShowId(gSaveBlock1Ptr->tvShows[j].common.kind) != TVGROUP_OUTBREAK)
        {
            if (gSaveBlock1Ptr->tvShows[j].common.active == TRUE)
                return j;
        }
        else
        {
            show = &gSaveBlock1Ptr->tvShows[j];
            if (show->massOutbreak.daysLeft == 0 && show->massOutbreak.active == TRUE)
                return j;
        }

        if (j == 0)
            j = ARRAY_COUNT(gSaveBlock1Ptr->tvShows) - 2;
        else
            j--;

    } while (j != selIdx);
    return 0xFF;
}

u8 FindAnyTVShowOnTheAir(void)
{
    u8 slot = GetRandomActiveShowIdx();
    if (slot == 0xFF)
        return 0xFF;

    if (gSaveBlock1Ptr->outbreakPokemonSpecies != SPECIES_NONE
     && gSaveBlock1Ptr->tvShows[slot].common.kind == TVSHOW_MASS_OUTBREAK)
        return FindFirstActiveTVShowThatIsNotAMassOutbreak();

    return slot;
}

void UpdateTVScreensOnMap(int width, int height)
{
    FlagSet(FLAG_SYS_TV_WATCH);
    switch (CheckForPlayersHouseNews())
    {
    case PLAYERS_HOUSE_TV_LATI:
        SetTVMetatilesOnMap(width, height, METATILE_Building_TV_On);
        break;
    case PLAYERS_HOUSE_TV_MOVIE:
        // Don't flash TV for movie text in player's house
        break;
//  case PLAYERS_HOUSE_TV_NONE:
    default:
        if (gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(LILYCOVE_CITY_COVE_LILY_MOTEL_1F)
         && gSaveBlock1Ptr->location.mapNum == MAP_NUM(LILYCOVE_CITY_COVE_LILY_MOTEL_1F))
        {
            // NPC in Lilycove Hotel is always watching TV
            SetTVMetatilesOnMap(width, height, METATILE_Building_TV_On);
        }
        else if (FlagGet(FLAG_SYS_TV_START) && (FindAnyTVShowOnTheAir() != 0xFF || FindAnyPokeNewsOnTheAir() != 0xFF || IsGabbyAndTyShowOnTheAir()))
        {
            FlagClear(FLAG_SYS_TV_WATCH);
            SetTVMetatilesOnMap(width, height, METATILE_Building_TV_On);
        }
        break;
    }
}

static void SetTVMetatilesOnMap(int width, int height, u16 metatileId)
{
    int x;
    int y;

    for (y = 0; y < height; y++)
    {
        for (x = 0; x < width; x++)
        {
            if (MapGridGetMetatileBehaviorAt(x, y) == MB_TELEVISION)
                MapGridSetMetatileIdAt(x, y, metatileId | MAPGRID_COLLISION_MASK);
        }
    }
}

void TurnOffTVScreen(void)
{
    SetTVMetatilesOnMap(gBackupMapLayout.width, gBackupMapLayout.height, METATILE_Building_TV_Off);
    DrawWholeMapView();
}

void TurnOnTVScreen(void)
{
    SetTVMetatilesOnMap(gBackupMapLayout.width, gBackupMapLayout.height, METATILE_Building_TV_On);
    DrawWholeMapView();
}

// gSpecialVar_0x8004 here is set from GetRandomActiveShowIdx in EventScript_TryDoTVShow
u8 GetSelectedTVShow(void)
{
    return gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004].common.kind;
}

static u8 FindFirstActiveTVShowThatIsNotAMassOutbreak(void)
{
    u8 i;

    for (i = 0; i < ARRAY_COUNT(gSaveBlock1Ptr->tvShows) - 1; i++)
    {
        if (gSaveBlock1Ptr->tvShows[i].common.kind != TVSHOW_OFF_AIR
         && gSaveBlock1Ptr->tvShows[i].common.kind != TVSHOW_MASS_OUTBREAK
         && gSaveBlock1Ptr->tvShows[i].common.active == TRUE)
            return i;
    }
    return 0xFF;
}

u8 GetNextActiveShowIfMassOutbreak(void)
{
    TVShow *tvShow;

    tvShow = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    if (tvShow->common.kind == TVSHOW_MASS_OUTBREAK && gSaveBlock1Ptr->outbreakPokemonSpecies != SPECIES_NONE)
        return FindFirstActiveTVShowThatIsNotAMassOutbreak();

    return gSpecialVar_0x8004;
}

// IN SEARCH OF TRAINERS

void ResetGabbyAndTy(void)
{
    gSaveBlock1Ptr->gabbyAndTyData.mon1 = SPECIES_NONE;
    gSaveBlock1Ptr->gabbyAndTyData.mon2 = SPECIES_NONE;
    gSaveBlock1Ptr->gabbyAndTyData.lastMove = MOVE_NONE;
    gSaveBlock1Ptr->gabbyAndTyData.quote[0] = -1;
    gSaveBlock1Ptr->gabbyAndTyData.battleTookMoreThanOneTurn = FALSE;
    gSaveBlock1Ptr->gabbyAndTyData.playerLostAMon = FALSE;
    gSaveBlock1Ptr->gabbyAndTyData.playerUsedHealingItem = FALSE;
    gSaveBlock1Ptr->gabbyAndTyData.playerThrewABall = FALSE;
    gSaveBlock1Ptr->gabbyAndTyData.onAir = FALSE;
    gSaveBlock1Ptr->gabbyAndTyData.valA_5 = 0;
    gSaveBlock1Ptr->gabbyAndTyData.battleTookMoreThanOneTurn2 = FALSE;
    gSaveBlock1Ptr->gabbyAndTyData.playerLostAMon2 = FALSE;
    gSaveBlock1Ptr->gabbyAndTyData.playerUsedHealingItem2 = FALSE;
    gSaveBlock1Ptr->gabbyAndTyData.playerThrewABall2 = FALSE;
    gSaveBlock1Ptr->gabbyAndTyData.valB_4 = 0;
    gSaveBlock1Ptr->gabbyAndTyData.mapnum = 0;
    gSaveBlock1Ptr->gabbyAndTyData.battleNum = 0;
}

void GabbyAndTyBeforeInterview(void)
{
    u8 i;

    gSaveBlock1Ptr->gabbyAndTyData.mon1 = gBattleResults.playerMon1Species;
    gSaveBlock1Ptr->gabbyAndTyData.mon2 = gBattleResults.playerMon2Species;
    gSaveBlock1Ptr->gabbyAndTyData.lastMove = gBattleResults.lastUsedMovePlayer;
    if (gSaveBlock1Ptr->gabbyAndTyData.battleNum != 0xFF)
    {
        gSaveBlock1Ptr->gabbyAndTyData.battleNum++;
    }
    gSaveBlock1Ptr->gabbyAndTyData.battleTookMoreThanOneTurn = gBattleResults.playerMonWasDamaged;

    if (gBattleResults.playerFaintCounter != 0)
        gSaveBlock1Ptr->gabbyAndTyData.playerLostAMon = TRUE;
    else
        gSaveBlock1Ptr->gabbyAndTyData.playerLostAMon = FALSE;

    if (gBattleResults.numHealingItemsUsed != 0)
        gSaveBlock1Ptr->gabbyAndTyData.playerUsedHealingItem = TRUE;
    else
        gSaveBlock1Ptr->gabbyAndTyData.playerUsedHealingItem = FALSE;

    if (!gBattleResults.usedMasterBall)
    {
        for (i = 0; i < POKEBALL_COUNT - 1; i++)
        {
            if (gBattleResults.catchAttempts[i])
            {
                gSaveBlock1Ptr->gabbyAndTyData.playerThrewABall = TRUE;
                break;
            }
        }
    }
    else
    {
        // Player threw a Master Ball at Gabby and Ty
        gSaveBlock1Ptr->gabbyAndTyData.playerThrewABall = TRUE;
    }

    TakeGabbyAndTyOffTheAir();
    if (gSaveBlock1Ptr->gabbyAndTyData.lastMove == MOVE_NONE)
    {
        FlagSet(FLAG_TEMP_1);
    }
}

void GabbyAndTyAfterInterview(void)
{
    gSaveBlock1Ptr->gabbyAndTyData.battleTookMoreThanOneTurn2 = gSaveBlock1Ptr->gabbyAndTyData.battleTookMoreThanOneTurn;
    gSaveBlock1Ptr->gabbyAndTyData.playerLostAMon2 = gSaveBlock1Ptr->gabbyAndTyData.playerLostAMon;
    gSaveBlock1Ptr->gabbyAndTyData.playerUsedHealingItem2 = gSaveBlock1Ptr->gabbyAndTyData.playerUsedHealingItem;
    gSaveBlock1Ptr->gabbyAndTyData.playerThrewABall2 = gSaveBlock1Ptr->gabbyAndTyData.playerThrewABall;
    gSaveBlock1Ptr->gabbyAndTyData.onAir = TRUE;
    gSaveBlock1Ptr->gabbyAndTyData.mapnum = gMapHeader.regionMapSectionId;
    IncrementGameStat(GAME_STAT_GOT_INTERVIEWED);
}

static void TakeGabbyAndTyOffTheAir(void)
{
    gSaveBlock1Ptr->gabbyAndTyData.onAir = FALSE;
}

u8 GabbyAndTyGetBattleNum(void)
{
    if (gSaveBlock1Ptr->gabbyAndTyData.battleNum > 5)
        return (gSaveBlock1Ptr->gabbyAndTyData.battleNum % 3) + 6;

    return gSaveBlock1Ptr->gabbyAndTyData.battleNum;
}

bool8 IsGabbyAndTyShowOnTheAir(void)
{
    return gSaveBlock1Ptr->gabbyAndTyData.onAir;
}

bool8 GabbyAndTyGetLastQuote(void)
{
    if (gSaveBlock1Ptr->gabbyAndTyData.quote[0] == EC_EMPTY_WORD)
    {
        return FALSE;
    }
    CopyEasyChatWord(gStringVar1, gSaveBlock1Ptr->gabbyAndTyData.quote[0]);
    gSaveBlock1Ptr->gabbyAndTyData.quote[0] = -1;
    return TRUE;
}

u8 GabbyAndTyGetLastBattleTrivia(void)
{
    if (!gSaveBlock1Ptr->gabbyAndTyData.battleTookMoreThanOneTurn2)
        return 1;

    if (gSaveBlock1Ptr->gabbyAndTyData.playerThrewABall2)
        return 2;

    if (gSaveBlock1Ptr->gabbyAndTyData.playerUsedHealingItem2)
        return 3;

    if (gSaveBlock1Ptr->gabbyAndTyData.playerLostAMon2)
        return 4;

    return 0;
}

void GetGabbyAndTyLocalIds(void)
{
    switch (GabbyAndTyGetBattleNum())
    {
    case 1:
        gSpecialVar_0x8004 = 14;
        gSpecialVar_0x8005 = 13;
        break;
    case 2:
        gSpecialVar_0x8004 = 5;
        gSpecialVar_0x8005 = 6;
        break;
    case 3:
        gSpecialVar_0x8004 = 18;
        gSpecialVar_0x8005 = 17;
        break;
    case 4:
        gSpecialVar_0x8004 = 21;
        gSpecialVar_0x8005 = 22;
        break;
    case 5:
        gSpecialVar_0x8004 = 8;
        gSpecialVar_0x8005 = 9;
        break;
    case 6:
        gSpecialVar_0x8004 = 19;
        gSpecialVar_0x8005 = 20;
        break;
    case 7:
        gSpecialVar_0x8004 = 23;
        gSpecialVar_0x8005 = 24;
        break;
    case 8:
        gSpecialVar_0x8004 = 10;
        gSpecialVar_0x8005 = 11;
        break;
    }
}

void InterviewAfter(void)
{
    switch (gSpecialVar_0x8005)
    {
    case TVSHOW_FAN_CLUB_LETTER:
        InterviewAfter_FanClubLetter();
        break;
    case TVSHOW_RECENT_HAPPENINGS:
        InterviewAfter_RecentHappenings();
        break;
    case TVSHOW_PKMN_FAN_CLUB_OPINIONS:
        InterviewAfter_PkmnFanClubOpinions();
        break;
    case TVSHOW_DUMMY:
        InterviewAfter_Dummy();
        break;
    case TVSHOW_BRAVO_TRAINER_POKEMON_PROFILE:
        InterviewAfter_BravoTrainerPokemonProfile();
        break;
    case TVSHOW_BRAVO_TRAINER_BATTLE_TOWER_PROFILE:
        InterviewAfter_BravoTrainerBattleTowerProfile();
        break;
    case TVSHOW_CONTEST_LIVE_UPDATES:
        InterviewAfter_ContestLiveUpdates();
        break;
    }
}

void TryPutPokemonTodayOnAir(void)
{
    u8 i;
    u16 ballsUsed;
    TVShow *show;
    u32 language2;
    u16 itemLastUsed;

    ballsUsed = 0;
    TryPutRandomPokeNewsOnAir();
    TryStartRandomMassOutbreak();

    // Try either the Failed or Caught version of the show
    if (gBattleResults.caughtMonSpecies == SPECIES_NONE)
    {
        TryPutPokemonTodayFailedOnTheAir();
    }
    else
    {
        InitWorldOfMastersShowAttempt();
        if (!rbernoulli(1, 1) && StringCompare(gSpeciesNames[gBattleResults.caughtMonSpecies], gBattleResults.caughtMonNick))
        {
            sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
            if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_POKEMON_TODAY_CAUGHT, FALSE) != TRUE)
            {
                for (i = 0; i < POKEBALL_COUNT - 1; i++)
                    ballsUsed += gBattleResults.catchAttempts[i];

                if (ballsUsed != 0 || gBattleResults.usedMasterBall)
                {
                    ballsUsed = 0;
                    show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
                    show->pokemonToday.kind = TVSHOW_POKEMON_TODAY_CAUGHT;
                    show->pokemonToday.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
                    if (gBattleResults.usedMasterBall)
                    {
                        ballsUsed = 1;
                        itemLastUsed = ITEM_MASTER_BALL;
                    }
                    else
                    {
                        for (i = 0; i < POKEBALL_COUNT - 1; i++)
                            ballsUsed += gBattleResults.catchAttempts[i];
                        if (ballsUsed > 255)
                            ballsUsed = 255;
                        itemLastUsed = gLastUsedItem;
                    }
                    show->pokemonToday.nBallsUsed = ballsUsed;
                    show->pokemonToday.ball = itemLastUsed;
                    StringCopy(show->pokemonToday.playerName, gSaveBlock2Ptr->playerName);
                    StringCopy(show->pokemonToday.nickname, gBattleResults.caughtMonNick);
                    language2 = GetNicknameLanguage(show->pokemonToday.nickname);
                    StripExtCtrlCodes(show->pokemonToday.nickname);
                    show->pokemonToday.species = gBattleResults.caughtMonSpecies;
                    StorePlayerIdInRecordMixShow(show);
                    show->pokemonToday.language = gGameLanguage;
                    show->pokemonToday.language2 = language2;
                }
            }
        }
    }
}

// Show is initialized in last slot and updated there until it's
// either triggered or deleted at the end of the day by ResolveWorldOfMastersShow
static void InitWorldOfMastersShowAttempt(void)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[LAST_TVSHOW_IDX];
    if (show->common.kind != TVSHOW_WORLD_OF_MASTERS)
    {
        DeleteTVShowInArrayByIdx(gSaveBlock1Ptr->tvShows, LAST_TVSHOW_IDX);
        show->worldOfMasters.steps = GetGameStat(GAME_STAT_STEPS);
        show->worldOfMasters.kind = TVSHOW_WORLD_OF_MASTERS;
    }
    show->worldOfMasters.numPokeCaught++;
    show->worldOfMasters.caughtPoke = gBattleResults.caughtMonSpecies;
    show->worldOfMasters.species = gBattleResults.playerMon1Species;
    show->worldOfMasters.location = gMapHeader.regionMapSectionId;
}

static void TryPutPokemonTodayFailedOnTheAir(void)
{
    u16 ballsUsed;
    u8 i;
    TVShow *show;

    if (!rbernoulli(1, 1))
    {
        for (i = 0, ballsUsed = 0; i < POKEBALL_COUNT - 1; i++)
            ballsUsed += gBattleResults.catchAttempts[i];
        if (ballsUsed > 255)
            ballsUsed = 255;

        if (ballsUsed > 2 && (gBattleOutcome == B_OUTCOME_MON_FLED || gBattleOutcome == B_OUTCOME_WON))
        {
            sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
            if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_POKEMON_TODAY_FAILED, FALSE) != TRUE)
            {
                show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
                show->pokemonTodayFailed.kind = TVSHOW_POKEMON_TODAY_FAILED;
                show->pokemonTodayFailed.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
                show->pokemonTodayFailed.species = gBattleResults.playerMon1Species;
                show->pokemonTodayFailed.species2 = gBattleResults.lastOpponentSpecies;
                show->pokemonTodayFailed.nBallsUsed = ballsUsed;
                show->pokemonTodayFailed.outcome = gBattleOutcome;
                show->pokemonTodayFailed.location = gMapHeader.regionMapSectionId;
                StringCopy(show->pokemonTodayFailed.playerName, gSaveBlock2Ptr->playerName);
                StorePlayerIdInRecordMixShow(show);
                show->pokemonTodayFailed.language = gGameLanguage;
            }
        }
    }
}

static void StorePlayerIdInRecordMixShow(TVShow *show)
{
    u32 id = GetPlayerIDAsU32();
    show->common.srcTrainerId2Lo = id;
    show->common.srcTrainerId2Hi = id >> 8;
    show->common.srcTrainerIdLo = id;
    show->common.srcTrainerIdHi = id >> 8;
    show->common.trainerIdLo = id;
    show->common.trainerIdHi = id >> 8;
}

static void StorePlayerIdInNormalShow(TVShow *show)
{
    u32 id = GetPlayerIDAsU32();
    show->common.srcTrainerIdLo = id;
    show->common.srcTrainerIdHi = id >> 8;
    show->common.trainerIdLo = id;
    show->common.trainerIdHi = id >> 8;
}

static void InterviewAfter_ContestLiveUpdates(void)
{
    TVShow *show;
    TVShow *show2;

    show = &gSaveBlock1Ptr->tvShows[LAST_TVSHOW_IDX];
    if (show->contestLiveUpdates.kind == TVSHOW_CONTEST_LIVE_UPDATES)
    {
        show2 = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show2->contestLiveUpdates.kind = TVSHOW_CONTEST_LIVE_UPDATES;
        show2->contestLiveUpdates.active = TRUE;
        StringCopy(show2->contestLiveUpdates.winningTrainerName, gSaveBlock2Ptr->playerName); // Show only begins running if player won, so always load players name
        show2->contestLiveUpdates.category = gSpecialVar_ContestCategory;
        show2->contestLiveUpdates.winningSpecies = GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_SPECIES, NULL);
        show2->contestLiveUpdates.losingSpecies = show->contestLiveUpdates.losingSpecies;
        show2->contestLiveUpdates.loserAppealFlag = show->contestLiveUpdates.loserAppealFlag;
        show2->contestLiveUpdates.round1Placing = show->contestLiveUpdates.round1Placing;
        show2->contestLiveUpdates.round2Placing = show->contestLiveUpdates.round2Placing;
        show2->contestLiveUpdates.move = show->contestLiveUpdates.move;
        show2->contestLiveUpdates.winnerAppealFlag = show->contestLiveUpdates.winnerAppealFlag;
        StringCopy(show2->contestLiveUpdates.losingTrainerName, show->contestLiveUpdates.losingTrainerName);
        StorePlayerIdInNormalShow(show2);
        show2->contestLiveUpdates.winningTrainerLanguage = gGameLanguage;
        show2->contestLiveUpdates.losingTrainerLanguage = show->contestLiveUpdates.losingTrainerLanguage;
        DeleteTVShowInArrayByIdx(gSaveBlock1Ptr->tvShows, LAST_TVSHOW_IDX);
    }
}

void PutBattleUpdateOnTheAir(u8 opponentLinkPlayerId, u16 move, u16 speciesPlayer, u16 speciesOpponent)
{
    TVShow *show;
    u8 name[32];

    sCurTVShowSlot = FindFirstEmptyNormalTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1)
    {
        TryReplaceOldTVShowOfKind(TVSHOW_BATTLE_UPDATE);
        if (gSpecialVar_Result != TRUE)
        {
            show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
            show->battleUpdate.kind = TVSHOW_BATTLE_UPDATE;
            show->battleUpdate.active = TRUE;
            StringCopy(show->battleUpdate.playerName, gSaveBlock2Ptr->playerName);

            if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
                show->battleUpdate.battleType = 2;
            else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
                show->battleUpdate.battleType = 1;
            else
                show->battleUpdate.battleType = 0;

            show->battleUpdate.move = move;
            show->battleUpdate.speciesPlayer = speciesPlayer;
            show->battleUpdate.speciesOpponent = speciesOpponent;
            StringCopy(name, gLinkPlayers[opponentLinkPlayerId].name);
            StripExtCtrlCodes(name);
            StringCopy(show->battleUpdate.linkOpponentName, name);
            StorePlayerIdInNormalShow(show);
            show->battleUpdate.language = gGameLanguage;
            if (show->battleUpdate.language == LANGUAGE_JAPANESE || gLinkPlayers[opponentLinkPlayerId].language == LANGUAGE_JAPANESE)
                show->battleUpdate.linkOpponentLanguage = LANGUAGE_JAPANESE;
            else
                show->battleUpdate.linkOpponentLanguage = gLinkPlayers[opponentLinkPlayerId].language;
        }
    }
}

bool8 Put3CheersForPokeblocksOnTheAir(const u8 *partnersName, u8 flavor, u8 color, u8 sheen, u8 language)
{
    TVShow *show;
    u8 name[32];

    sCurTVShowSlot = FindFirstEmptyNormalTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot == -1)
        return FALSE;

    TryReplaceOldTVShowOfKind(TVSHOW_3_CHEERS_FOR_POKEBLOCKS);
    if (gSpecialVar_Result == TRUE)
        return FALSE; // Old show is still active

    show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
    show->threeCheers.kind = TVSHOW_3_CHEERS_FOR_POKEBLOCKS;
    show->threeCheers.active = TRUE;
    StringCopy(show->threeCheers.playerName, gSaveBlock2Ptr->playerName);
    StringCopy(name, partnersName);
    StripExtCtrlCodes(name);
    StringCopy(show->threeCheers.worstBlenderName, name);
    show->threeCheers.flavor = flavor;
    show->threeCheers.color = color;
    show->threeCheers.sheen = sheen;
    StorePlayerIdInNormalShow(show);
    show->threeCheers.language = gGameLanguage;
    if (show->threeCheers.language == LANGUAGE_JAPANESE || language == LANGUAGE_JAPANESE)
        show->threeCheers.worstBlenderLanguage = LANGUAGE_JAPANESE;
    else
        show->threeCheers.worstBlenderLanguage = language;
    return TRUE;
}

void PutFanClubSpecialOnTheAir(void)
{
    TVShow *show;
    u8 name[32];
    u32 id;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8006];
    show->fanClubSpecial.score = gSpecialVar_0x8005 * 10;
    StringCopy(show->fanClubSpecial.playerName, gSaveBlock2Ptr->playerName);
    show->fanClubSpecial.kind = TVSHOW_FAN_CLUB_SPECIAL;
    show->fanClubSpecial.active = TRUE;
    id = GetPlayerIDAsU32();
    show->fanClubSpecial.idLo = id;
    show->fanClubSpecial.idHi = id >> 8;
    StringCopy(name, gStringVar1);
    StripExtCtrlCodes(name);
    StringCopy(show->fanClubSpecial.idolName, name);
    StorePlayerIdInNormalShow(show);
    show->fanClubSpecial.language = gGameLanguage;
    if (show->fanClubSpecial.language == LANGUAGE_JAPANESE || gSaveBlock1Ptr->linkBattleRecords.languages[0] == LANGUAGE_JAPANESE)
        show->fanClubSpecial.idolNameLanguage = LANGUAGE_JAPANESE;
    else
        show->fanClubSpecial.idolNameLanguage = gSaveBlock1Ptr->linkBattleRecords.languages[0];
}

void ContestLiveUpdates_Init(u8 round1Placing)
{
    TVShow *show;

    DeleteTVShowInArrayByIdx(gSaveBlock1Ptr->tvShows, LAST_TVSHOW_IDX);
    sCurTVShowSlot = FindFirstEmptyNormalTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1)
    {
        show = &gSaveBlock1Ptr->tvShows[LAST_TVSHOW_IDX];
        show->contestLiveUpdates.round1Placing = round1Placing;
        show->contestLiveUpdates.kind = TVSHOW_CONTEST_LIVE_UPDATES;
    }
}

void ContestLiveUpdates_SetRound2Placing(u8 round2Placing)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[LAST_TVSHOW_IDX];
    sCurTVShowSlot = FindFirstEmptyNormalTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1)
        show->contestLiveUpdates.round2Placing = round2Placing;
}

void ContestLiveUpdates_SetWinnerAppealFlag(u8 flag)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[LAST_TVSHOW_IDX];
    sCurTVShowSlot = FindFirstEmptyNormalTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1)
        show->contestLiveUpdates.winnerAppealFlag = flag;
}

void ContestLiveUpdates_SetWinnerMoveUsed(u16 move)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[LAST_TVSHOW_IDX];
    sCurTVShowSlot = FindFirstEmptyNormalTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1)
        show->contestLiveUpdates.move = move;
}

void ContestLiveUpdates_SetLoserData(u8 flag, u8 loser)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[LAST_TVSHOW_IDX];
    sCurTVShowSlot = FindFirstEmptyNormalTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1)
    {
        show->contestLiveUpdates.losingSpecies = gContestMons[loser].species;
        StringCopy(show->contestLiveUpdates.losingTrainerName, gContestMons[loser].trainerName);
        StripExtCtrlCodes(show->contestLiveUpdates.losingTrainerName);
        show->contestLiveUpdates.loserAppealFlag = flag;

        if (loser + 1 > gNumLinkContestPlayers)
            show->contestLiveUpdates.losingTrainerLanguage = gLinkPlayers[0].language;
        else if (gGameLanguage == LANGUAGE_JAPANESE || gLinkPlayers[loser].language == LANGUAGE_JAPANESE)
            show->contestLiveUpdates.losingTrainerLanguage = LANGUAGE_JAPANESE;
        else
            show->contestLiveUpdates.losingTrainerLanguage = gLinkPlayers[loser].language;
    }
}

static void InterviewAfter_BravoTrainerPokemonProfile(void)
{
    TVShow *show;
    TVShow *show2;

    show = &gSaveBlock1Ptr->tvShows[LAST_TVSHOW_IDX];
    if (show->bravoTrainer.kind == TVSHOW_BRAVO_TRAINER_POKEMON_PROFILE)
    {
        show2 = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show2->bravoTrainer.kind = TVSHOW_BRAVO_TRAINER_POKEMON_PROFILE;
        show2->bravoTrainer.active = TRUE;
        show2->bravoTrainer.species = show->bravoTrainer.species;
        StringCopy(show2->bravoTrainer.playerName, gSaveBlock2Ptr->playerName);
        StringCopy(show2->bravoTrainer.pokemonNickname, show->bravoTrainer.pokemonNickname);
        show2->bravoTrainer.contestCategory = show->bravoTrainer.contestCategory;
        show2->bravoTrainer.contestRank = show->bravoTrainer.contestRank;
        show2->bravoTrainer.move = show->bravoTrainer.move;
        show2->bravoTrainer.contestResult = show->bravoTrainer.contestResult;
        show2->bravoTrainer.contestCategory = show->bravoTrainer.contestCategory;
        StorePlayerIdInNormalShow(show2);
        show2->bravoTrainer.language = gGameLanguage;
        if (show2->bravoTrainer.language == LANGUAGE_JAPANESE || show->bravoTrainer.pokemonNameLanguage == LANGUAGE_JAPANESE)
            show2->bravoTrainer.pokemonNameLanguage = LANGUAGE_JAPANESE;
        else
            show2->bravoTrainer.pokemonNameLanguage = show->bravoTrainer.pokemonNameLanguage;
        DeleteTVShowInArrayByIdx(gSaveBlock1Ptr->tvShows, LAST_TVSHOW_IDX);
    }
}

void BravoTrainerPokemonProfile_BeforeInterview1(u16 a0)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[LAST_TVSHOW_IDX];
    InterviewBefore_BravoTrainerPkmnProfile();
    sCurTVShowSlot = FindFirstEmptyNormalTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1)
    {
        DeleteTVShowInArrayByIdx(gSaveBlock1Ptr->tvShows, LAST_TVSHOW_IDX);
        show->bravoTrainer.move = a0;
        show->bravoTrainer.kind = TVSHOW_BRAVO_TRAINER_POKEMON_PROFILE;
    }
}

void BravoTrainerPokemonProfile_BeforeInterview2(u8 contestStandingPlace)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[LAST_TVSHOW_IDX];
    sCurTVShowSlot = FindFirstEmptyNormalTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1)
    {
        show->bravoTrainer.contestResult = contestStandingPlace;
        show->bravoTrainer.contestCategory = gSpecialVar_ContestCategory;
        show->bravoTrainer.contestRank = gSpecialVar_ContestRank;
        show->bravoTrainer.species = GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_SPECIES, NULL);
        GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_NICKNAME, show->bravoTrainer.pokemonNickname);
        StripExtCtrlCodes(show->bravoTrainer.pokemonNickname);
        show->bravoTrainer.pokemonNameLanguage = GetMonData(&gPlayerParty[gContestMonPartyIndex], MON_DATA_LANGUAGE);
    }
}

static void InterviewAfter_BravoTrainerBattleTowerProfile(void)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
    show->bravoTrainerTower.kind = TVSHOW_BRAVO_TRAINER_BATTLE_TOWER_PROFILE;
    show->bravoTrainerTower.active = TRUE;
    StringCopy(show->bravoTrainerTower.trainerName, gSaveBlock2Ptr->playerName);
    StringCopy(show->bravoTrainerTower.pokemonName, gSaveBlock2Ptr->frontier.towerInterview.opponentName);
    show->bravoTrainerTower.species = gSaveBlock2Ptr->frontier.towerInterview.playerSpecies;
    show->bravoTrainerTower.defeatedSpecies = gSaveBlock2Ptr->frontier.towerInterview.opponentSpecies;
    show->bravoTrainerTower.numFights = GetCurrentBattleTowerWinStreak(gSaveBlock2Ptr->frontier.towerLvlMode, 0);
    show->bravoTrainerTower.wonTheChallenge = gSaveBlock2Ptr->frontier.towerBattleOutcome;
    if (gSaveBlock2Ptr->frontier.towerLvlMode == FRONTIER_LVL_50)
        show->bravoTrainerTower.btLevel = 50;
    else
        show->bravoTrainerTower.btLevel = 100;
    show->bravoTrainerTower.interviewResponse = gSpecialVar_0x8004;
    StorePlayerIdInNormalShow(show);
    show->bravoTrainerTower.language = gGameLanguage;
    if (show->bravoTrainerTower.language == LANGUAGE_JAPANESE || gSaveBlock2Ptr->frontier.towerInterview.opponentLanguage == LANGUAGE_JAPANESE)
        show->bravoTrainerTower.pokemonNameLanguage = LANGUAGE_JAPANESE;
    else
        show->bravoTrainerTower.pokemonNameLanguage = gSaveBlock2Ptr->frontier.towerInterview.opponentLanguage;
}

void TryPutSmartShopperOnAir(void)
{
    TVShow *show;
    u8 i;

    if (!(gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(TRAINER_HILL_ENTRANCE) && gSaveBlock1Ptr->location.mapNum == MAP_NUM(TRAINER_HILL_ENTRANCE))
     && !(gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(BATTLE_FRONTIER_MART) && gSaveBlock1Ptr->location.mapNum == MAP_NUM(BATTLE_FRONTIER_MART))
     && !rbernoulli(1, 3))
    {
        sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
        if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_SMART_SHOPPER, FALSE) != TRUE)
        {
            SortPurchasesByQuantity();
            if (gMartPurchaseHistory[0].quantity >= 20)
            {
                show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
                show->smartshopperShow.kind = TVSHOW_SMART_SHOPPER;
                show->smartshopperShow.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
                show->smartshopperShow.shopLocation = gMapHeader.regionMapSectionId;
                for (i = 0; i < SMARTSHOPPER_NUM_ITEMS; i++)
                {
                    show->smartshopperShow.itemIds[i] = gMartPurchaseHistory[i].itemId;
                    show->smartshopperShow.itemAmounts[i] = gMartPurchaseHistory[i].quantity;
                }
                show->smartshopperShow.priceReduced = IsPokeNewsActive(POKENEWS_SLATEPORT);
                StringCopy(show->smartshopperShow.playerName, gSaveBlock2Ptr->playerName);
                StorePlayerIdInRecordMixShow(show);
                show->smartshopperShow.language = gGameLanguage;
            }
        }
    }
}

void PutNameRaterShowOnTheAir(void)
{
    TVShow *show;

    InterviewBefore_NameRater();
    if (gSpecialVar_Result != 1)
    {
        GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_NICKNAME, gStringVar1);
        if (StringLength(gSaveBlock2Ptr->playerName) > 1 && StringLength(gStringVar1) > 1)
        {
            show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
            show->nameRaterShow.kind = TVSHOW_NAME_RATER_SHOW;
            show->nameRaterShow.active = TRUE;
            show->nameRaterShow.species = GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPECIES, NULL);
            show->nameRaterShow.random = Random() % 3;
            show->nameRaterShow.random2 = Random() % 2;
            show->nameRaterShow.randomSpecies = GetRandomDifferentSpeciesSeenByPlayer(show->nameRaterShow.species);
            StringCopy(show->nameRaterShow.trainerName, gSaveBlock2Ptr->playerName);
            GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_NICKNAME, show->nameRaterShow.pokemonName);
            StripExtCtrlCodes(show->nameRaterShow.pokemonName);
            StorePlayerIdInNormalShow(show);
            show->nameRaterShow.language = gGameLanguage;
            show->nameRaterShow.pokemonNameLanguage = GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_LANGUAGE);
        }
    }
}

void StartMassOutbreak(void)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSaveBlock1Ptr->outbreakPokemonSpecies = show->massOutbreak.species;
    gSaveBlock1Ptr->outbreakLocationMapNum = show->massOutbreak.locationMapNum;
    gSaveBlock1Ptr->outbreakLocationMapGroup = show->massOutbreak.locationMapGroup;
    gSaveBlock1Ptr->outbreakPokemonLevel = show->massOutbreak.level;
    gSaveBlock1Ptr->outbreakUnused1 = show->massOutbreak.unused1;
    gSaveBlock1Ptr->outbreakUnused2 = show->massOutbreak.unused2;
    gSaveBlock1Ptr->outbreakPokemonMoves[0] = show->massOutbreak.moves[0];
    gSaveBlock1Ptr->outbreakPokemonMoves[1] = show->massOutbreak.moves[1];
    gSaveBlock1Ptr->outbreakPokemonMoves[2] = show->massOutbreak.moves[2];
    gSaveBlock1Ptr->outbreakPokemonMoves[3] = show->massOutbreak.moves[3];
    gSaveBlock1Ptr->outbreakUnused3 = show->massOutbreak.unused3;
    gSaveBlock1Ptr->outbreakPokemonProbability = show->massOutbreak.probability;
    gSaveBlock1Ptr->outbreakDaysLeft = 2;
}

void PutLilycoveContestLadyShowOnTheAir(void)
{
    TVShow *show;

    Script_FindFirstEmptyNormalTVShowSlot();
    if (gSpecialVar_Result != TRUE)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        BufferContestLadyLanguage(&show->contestLady.language);
        show->contestLady.pokemonNameLanguage = GAME_LANGUAGE;
        show->contestLady.kind = TVSHOW_LILYCOVE_CONTEST_LADY;
        show->contestLady.active = TRUE;
        BufferContestLadyPlayerName(show->contestLady.playerName);
        BufferContestLadyMonName(&show->contestLady.contestCategory, show->contestLady.nickname);
        show->contestLady.pokeblockState = GetContestLadyPokeblockState();
        StorePlayerIdInNormalShow(show);
    }
}

static void InterviewAfter_FanClubLetter(void)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
    show->fanclubLetter.kind = TVSHOW_FAN_CLUB_LETTER;
    show->fanclubLetter.active = TRUE;
    StringCopy(show->fanclubLetter.playerName, gSaveBlock2Ptr->playerName);
    show->fanclubLetter.species = GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_SPECIES, NULL);
    StorePlayerIdInNormalShow(show);
    show->fanclubLetter.language = gGameLanguage;
}

static void InterviewAfter_RecentHappenings(void)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
    show->recentHappenings.kind = TVSHOW_RECENT_HAPPENINGS;
    show->recentHappenings.active = TRUE;
    StringCopy(show->recentHappenings.playerName, gSaveBlock2Ptr->playerName);
    show->recentHappenings.species = SPECIES_NONE;
    StorePlayerIdInNormalShow(show);
    show->recentHappenings.language = gGameLanguage;
}

static void InterviewAfter_PkmnFanClubOpinions(void)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
    show->fanclubOpinions.kind = TVSHOW_PKMN_FAN_CLUB_OPINIONS;
    show->fanclubOpinions.active = TRUE;
    show->fanclubOpinions.friendshipHighNybble = GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_FRIENDSHIP, NULL) >> 4;
    show->fanclubOpinions.questionAsked = gSpecialVar_0x8007;
    StringCopy(show->fanclubOpinions.playerName, gSaveBlock2Ptr->playerName);
    GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_NICKNAME, show->fanclubOpinions.nickname);
    StripExtCtrlCodes(show->fanclubOpinions.nickname);
    show->fanclubOpinions.species = GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_SPECIES, NULL);
    StorePlayerIdInNormalShow(show);
    show->fanclubOpinions.language = gGameLanguage;
    if (gGameLanguage == LANGUAGE_JAPANESE || GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_LANGUAGE) == LANGUAGE_JAPANESE)
        show->fanclubOpinions.pokemonNameLanguage = LANGUAGE_JAPANESE;
    else
        show->fanclubOpinions.pokemonNameLanguage = GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_LANGUAGE);
}

static void InterviewAfter_Dummy(void)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
}

static void TryStartRandomMassOutbreak(void)
{
    u8 i;
    u16 outbreakIdx;
    TVShow *show;

    if (FlagGet(FLAG_SYS_GAME_CLEAR))
    {
        for (i = 0; i < LAST_TVSHOW_IDX; i++)
        {
            if (gSaveBlock1Ptr->tvShows[i].common.kind == TVSHOW_MASS_OUTBREAK)
                return;
        }
        if (!rbernoulli(1, 200))
        {
            sCurTVShowSlot = FindFirstEmptyNormalTVShowSlot(gSaveBlock1Ptr->tvShows);
            if (sCurTVShowSlot != -1)
            {
                outbreakIdx = Random() % ARRAY_COUNT(sPokeOutbreakSpeciesList);
                show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
                show->massOutbreak.kind = TVSHOW_MASS_OUTBREAK;
                show->massOutbreak.active = TRUE;
                show->massOutbreak.level = sPokeOutbreakSpeciesList[outbreakIdx].level;
                show->massOutbreak.unused1 = 0;
                show->massOutbreak.unused3 = 0;
                show->massOutbreak.species = sPokeOutbreakSpeciesList[outbreakIdx].species;
                show->massOutbreak.unused2 = 0;
                show->massOutbreak.moves[0] = sPokeOutbreakSpeciesList[outbreakIdx].moves[0];
                show->massOutbreak.moves[1] = sPokeOutbreakSpeciesList[outbreakIdx].moves[1];
                show->massOutbreak.moves[2] = sPokeOutbreakSpeciesList[outbreakIdx].moves[2];
                show->massOutbreak.moves[3] = sPokeOutbreakSpeciesList[outbreakIdx].moves[3];
                show->massOutbreak.locationMapNum = sPokeOutbreakSpeciesList[outbreakIdx].location;
                show->massOutbreak.locationMapGroup = 0;
                show->massOutbreak.unused4 = 0;
                show->massOutbreak.probability = 50;
                show->massOutbreak.unused5 = 0;
                show->massOutbreak.daysLeft = 1;
                StorePlayerIdInNormalShow(show);
                show->massOutbreak.language = gGameLanguage;
            }
        }
    }
}

void EndMassOutbreak(void)
{
    gSaveBlock1Ptr->outbreakPokemonSpecies = SPECIES_NONE;
    gSaveBlock1Ptr->outbreakLocationMapNum = 0;
    gSaveBlock1Ptr->outbreakLocationMapGroup = 0;
    gSaveBlock1Ptr->outbreakPokemonLevel = 0;
    gSaveBlock1Ptr->outbreakUnused1 = 0;
    gSaveBlock1Ptr->outbreakUnused2 = 0;
    gSaveBlock1Ptr->outbreakPokemonMoves[0] = MOVE_NONE;
    gSaveBlock1Ptr->outbreakPokemonMoves[1] = MOVE_NONE;
    gSaveBlock1Ptr->outbreakPokemonMoves[2] = MOVE_NONE;
    gSaveBlock1Ptr->outbreakPokemonMoves[3] = MOVE_NONE;
    gSaveBlock1Ptr->outbreakUnused3 = 0;
    gSaveBlock1Ptr->outbreakPokemonProbability = 0;
    gSaveBlock1Ptr->outbreakDaysLeft = 0;
}

void UpdateTVShowsPerDay(u16 days)
{
    UpdateMassOutbreakTimeLeft(days);
    TryEndMassOutbreak(days);
    UpdatePokeNewsCountdown(days);
    ResolveWorldOfMastersShow(days);
    ResolveNumberOneShow(days);
}

static void UpdateMassOutbreakTimeLeft(u16 days)
{
    u8 i;
    TVShow *show;

    if (gSaveBlock1Ptr->outbreakPokemonSpecies == SPECIES_NONE)
    {
        for (i = 0; i < LAST_TVSHOW_IDX; i++)
        {
            if (gSaveBlock1Ptr->tvShows[i].massOutbreak.kind == TVSHOW_MASS_OUTBREAK && gSaveBlock1Ptr->tvShows[i].massOutbreak.active == TRUE)
            {
                show = &gSaveBlock1Ptr->tvShows[i];
                if (show->massOutbreak.daysLeft < days)
                    show->massOutbreak.daysLeft = 0;
                else
                    show->massOutbreak.daysLeft -= days;

                break;
            }
        }
    }
}

static void TryEndMassOutbreak(u16 days)
{
    if (gSaveBlock1Ptr->outbreakDaysLeft <= days)
        EndMassOutbreak();
    else
        gSaveBlock1Ptr->outbreakDaysLeft -= days;
}

void RecordFishingAttemptForTV(bool8 caughtFish)
{
    if (caughtFish)
    {
        if (sPokemonAnglerAttemptCounters >> 8 > 4)
            TryPutFishingAdviceOnAir();

        sPokemonAnglerAttemptCounters &= 0xFF;
        if (sPokemonAnglerAttemptCounters != 0xFF)
            sPokemonAnglerAttemptCounters += 0x01;
    }
    else
    {
        if ((u8)sPokemonAnglerAttemptCounters > 4)
            TryPutFishingAdviceOnAir();

        sPokemonAnglerAttemptCounters &= 0xFF00;
        if (sPokemonAnglerAttemptCounters >> 8 != 0xFF)
            sPokemonAnglerAttemptCounters += 0x0100;
    }
}

static void TryPutFishingAdviceOnAir(void)
{
    TVShow *show;

    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_FISHING_ADVICE, FALSE) != TRUE)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->pokemonAngler.kind = TVSHOW_FISHING_ADVICE;
        show->pokemonAngler.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        show->pokemonAngler.nBites = sPokemonAnglerAttemptCounters;
        show->pokemonAngler.nFails = sPokemonAnglerAttemptCounters >> 8;
        show->pokemonAngler.species = sPokemonAnglerSpecies;
        StringCopy(show->pokemonAngler.playerName, gSaveBlock2Ptr->playerName);
        StorePlayerIdInRecordMixShow(show);
        show->pokemonAngler.language = gGameLanguage;
    }
}

void SetPokemonAnglerSpecies(u16 species)
{
    sPokemonAnglerSpecies = species;
}

// World of Masters is initialized in the last slot by InitWorldOfMastersShowAttempt
// If enough Pokémon were caught during the day the show can be put on air (and will
// be moved out of the last slot).
// Either way the temporary version of the show in the last slot is deleted.
static void ResolveWorldOfMastersShow(u16 days)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[LAST_TVSHOW_IDX];
    if (show->worldOfMasters.kind == TVSHOW_WORLD_OF_MASTERS)
    {
        if (show->worldOfMasters.numPokeCaught >= 20)
            TryPutWorldOfMastersOnAir();

        DeleteTVShowInArrayByIdx(gSaveBlock1Ptr->tvShows, LAST_TVSHOW_IDX);
    }
}

static void TryPutWorldOfMastersOnAir(void)
{
    TVShow *show;
    TVShow *show2;

    show = &gSaveBlock1Ptr->tvShows[LAST_TVSHOW_IDX];
    if (!rbernoulli(1, 1))
    {
        sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
        if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_WORLD_OF_MASTERS, FALSE) != TRUE)
        {
            show2 = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
            show2->worldOfMasters.kind = TVSHOW_WORLD_OF_MASTERS;
            show2->worldOfMasters.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
            show2->worldOfMasters.numPokeCaught = show->worldOfMasters.numPokeCaught;
            show2->worldOfMasters.steps = GetGameStat(GAME_STAT_STEPS) - show->worldOfMasters.steps;
            show2->worldOfMasters.caughtPoke = show->worldOfMasters.caughtPoke;
            show2->worldOfMasters.species = show->worldOfMasters.species;
            show2->worldOfMasters.location = show->worldOfMasters.location;
            StringCopy(show2->worldOfMasters.playerName, gSaveBlock2Ptr->playerName);
            StorePlayerIdInRecordMixShow(show2);
            show2->worldOfMasters.language = gGameLanguage;
            DeleteTVShowInArrayByIdx(gSaveBlock1Ptr->tvShows, LAST_TVSHOW_IDX);
        }
    }
}

void TryPutTodaysRivalTrainerOnAir(void)
{
    TVShow *show;
    u32 i;
    u8 nBadges;

    IsRecordMixShowAlreadySpawned(TVSHOW_TODAYS_RIVAL_TRAINER, TRUE); // Delete old version of show
    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->rivalTrainer.kind = TVSHOW_TODAYS_RIVAL_TRAINER;
        show->rivalTrainer.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        for (i = FLAG_BADGE01_GET, nBadges = 0; i < FLAG_BADGE01_GET + NUM_BADGES; i++)
        {
            if (FlagGet(i))
                nBadges++;
        }
        show->rivalTrainer.badgeCount = nBadges;
        if (IsNationalPokedexEnabled())
            show->rivalTrainer.dexCount = GetNationalPokedexCount(FLAG_GET_CAUGHT);
        else
            show->rivalTrainer.dexCount = GetHoennPokedexCount(FLAG_GET_CAUGHT);
        show->rivalTrainer.location = gMapHeader.regionMapSectionId;
        show->rivalTrainer.mapLayoutId = gMapHeader.mapLayoutId;
        show->rivalTrainer.nSilverSymbols = 0;
        show->rivalTrainer.nGoldSymbols = 0;
        for (i = 0; i < NUM_FRONTIER_FACILITIES; i++)
        {
            if (FlagGet(sSilverSymbolFlags[i]) == TRUE)
                show->rivalTrainer.nSilverSymbols++;

            if (FlagGet(sGoldSymbolFlags[i]) == TRUE)
                show->rivalTrainer.nGoldSymbols++;
        }
        show->rivalTrainer.battlePoints = gSaveBlock2Ptr->frontier.battlePoints;
        StringCopy(show->rivalTrainer.playerName, gSaveBlock2Ptr->playerName);
        StorePlayerIdInRecordMixShow(show);
        show->rivalTrainer.language = gGameLanguage;
    }
}

void TryPutTrendWatcherOnAir(const u16 *words)
{
    TVShow *show;

    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_TREND_WATCHER, FALSE) != TRUE)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->trendWatcher.kind = TVSHOW_TREND_WATCHER;
        show->trendWatcher.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        show->trendWatcher.gender = gSaveBlock2Ptr->playerGender;
        show->trendWatcher.words[0] = words[0];
        show->trendWatcher.words[1] = words[1];
        StringCopy(show->trendWatcher.playerName, gSaveBlock2Ptr->playerName);
        StorePlayerIdInRecordMixShow(show);
        show->trendWatcher.language = gGameLanguage;
    }
}

void TryPutTreasureInvestigatorsOnAir(void)
{
    TVShow *show;

    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_TREASURE_INVESTIGATORS, FALSE) != TRUE)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->treasureInvestigators.kind = TVSHOW_TREASURE_INVESTIGATORS;
        show->treasureInvestigators.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        show->treasureInvestigators.item = gSpecialVar_0x8005;
        show->treasureInvestigators.location = gMapHeader.regionMapSectionId;
        show->treasureInvestigators.mapLayoutId = gMapHeader.mapLayoutId;
        StringCopy(show->treasureInvestigators.playerName, gSaveBlock2Ptr->playerName);
        StorePlayerIdInRecordMixShow(show);
        show->treasureInvestigators.language = gGameLanguage;
    }
}

void TryPutFindThatGamerOnAir(u16 nCoinsPaidOut)
{
    TVShow *show;
    bool8 flag;
    u16 nCoinsWon;

    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_FIND_THAT_GAMER, FALSE) != TRUE)
    {
        flag = FALSE;
        switch (sFindThatGamerWhichGame)
        {
        case SLOT_MACHINE:
            if (nCoinsPaidOut >= sFindThatGamerCoinsSpent + 200)
            {
                flag = TRUE;
                nCoinsWon = nCoinsPaidOut - sFindThatGamerCoinsSpent;
                break;
            }
            if (sFindThatGamerCoinsSpent >= 100 && nCoinsPaidOut <= sFindThatGamerCoinsSpent - 100)
            {
                nCoinsWon = sFindThatGamerCoinsSpent - nCoinsPaidOut;
                break;
            }
            return;
        case ROULETTE:
            if (nCoinsPaidOut >= sFindThatGamerCoinsSpent + 50)
            {
                flag = TRUE;
                nCoinsWon = nCoinsPaidOut - sFindThatGamerCoinsSpent;
                break;
            }
            if (sFindThatGamerCoinsSpent >= 50 && nCoinsPaidOut <= sFindThatGamerCoinsSpent - 50)
            {
                nCoinsWon = sFindThatGamerCoinsSpent - nCoinsPaidOut;
                break;
            }
            return;
        default:
            return;
        }
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->findThatGamer.kind = TVSHOW_FIND_THAT_GAMER;
        show->findThatGamer.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        show->findThatGamer.nCoins = nCoinsWon;
        show->findThatGamer.whichGame = sFindThatGamerWhichGame;
        show->findThatGamer.won = flag;
        StringCopy(show->findThatGamer.playerName, gSaveBlock2Ptr->playerName);
        StorePlayerIdInRecordMixShow(show);
        show->findThatGamer.language = gGameLanguage;
    }
}

void AlertTVThatPlayerPlayedSlotMachine(u16 nCoinsSpent)
{
    sFindThatGamerWhichGame = SLOT_MACHINE;
    sFindThatGamerCoinsSpent = nCoinsSpent;
}

void AlertTVThatPlayerPlayedRoulette(u16 nCoinsSpent)
{
    sFindThatGamerWhichGame = ROULETTE;
    sFindThatGamerCoinsSpent = nCoinsSpent;
}

static void SecretBaseVisit_CalculateDecorationData(TVShow *show)
{
    u8 i, j;
    u16 k;
    u8 n;
    u8 decoration;

    for (i = 0; i < DECOR_MAX_SECRET_BASE; i++)
        sTV_DecorationsBuffer[i] = DECOR_NONE;

    // Count (and save) the unique decorations in the base
    for (i = 0, n = 0; i < DECOR_MAX_SECRET_BASE; i++)
    {
        decoration = gSaveBlock1Ptr->secretBases[0].decorations[i];
        if (decoration != DECOR_NONE)
        {
            // Search for an empty spot to save decoration
            for (j = 0; j < DECOR_MAX_SECRET_BASE; j++)
            {
                if (sTV_DecorationsBuffer[j] == DECOR_NONE)
                {
                    // Save and count new unique decoration
                    sTV_DecorationsBuffer[j] = decoration;
                    n++;
                    break;
                }

                // Decoration has already been saved, skip and move on to the next base decoration
                if (sTV_DecorationsBuffer[j] == decoration)
                    break;
            }
        }
    }

    // Cap the number of unique decorations to the number the TV show will talk about
    if (n > ARRAY_COUNT(show->secretBaseVisit.decorations))
        show->secretBaseVisit.numDecorations = ARRAY_COUNT(show->secretBaseVisit.decorations);
    else
        show->secretBaseVisit.numDecorations = n;

    switch (show->secretBaseVisit.numDecorations)
    {
    case 0:
        break;
    case 1:
        show->secretBaseVisit.decorations[0] = sTV_DecorationsBuffer[0];
        break;
    default:
        // More than 1 decoration, randomize the full list
        for (k = 0; k < n * n; k++)
        {
            decoration = Random() % n;
            j = Random() % n;
            SWAP(sTV_DecorationsBuffer[decoration], sTV_DecorationsBuffer[j], i);
        }

        // Pick the first decorations in the randomized list to talk about on the show
        for (i = 0; i < show->secretBaseVisit.numDecorations; i++)
            show->secretBaseVisit.decorations[i] = sTV_DecorationsBuffer[i];
        break;
    }
}

static void SecretBaseVisit_CalculatePartyData(TVShow *show)
{
    u8 i;
    u16 move;
    u16 j;
    u8 numMoves;
    u8 numPokemon;
    u16 sum;

    for (i = 0, numPokemon = 0; i < PARTY_SIZE; i++)
    {
        if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) != SPECIES_NONE && !GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG))
        {
            sTV_SecretBaseVisitMonsTemp[numPokemon].level = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);
            sTV_SecretBaseVisitMonsTemp[numPokemon].species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES);
            
            // Check all the Pokémon's moves, then randomly select one to save
            numMoves = 0;
            move = GetMonData(&gPlayerParty[i], MON_DATA_MOVE1);
            if (move != MOVE_NONE)
            {
                sTV_SecretBaseVisitMovesTemp[numMoves] = move;
                numMoves++;
            }
            move = GetMonData(&gPlayerParty[i], MON_DATA_MOVE2);
            if (move != MOVE_NONE)
            {
                sTV_SecretBaseVisitMovesTemp[numMoves] = move;
                numMoves++;
            }
            move = GetMonData(&gPlayerParty[i], MON_DATA_MOVE3);
            if (move != MOVE_NONE)
            {
                sTV_SecretBaseVisitMovesTemp[numMoves] = move;
                numMoves++;
            }
            move = GetMonData(&gPlayerParty[i], MON_DATA_MOVE4);
            if (move != MOVE_NONE)
            {
                sTV_SecretBaseVisitMovesTemp[numMoves] = move;
                numMoves++;
            }
            sTV_SecretBaseVisitMonsTemp[numPokemon].move = sTV_SecretBaseVisitMovesTemp[Random() % numMoves];
            numPokemon++;
        }
    }

    for (i = 0, sum = 0; i < numPokemon; i++)
        sum += sTV_SecretBaseVisitMonsTemp[i].level;

    // Using the data calculated above, save the data to talk about on the show 
    // (average level, and one randomly selected species / move)
    show->secretBaseVisit.avgLevel = sum / numPokemon;
    j = Random() % numPokemon;
    show->secretBaseVisit.species = sTV_SecretBaseVisitMonsTemp[j].species;
    show->secretBaseVisit.move = sTV_SecretBaseVisitMonsTemp[j].move;
}

void TryPutSecretBaseVisitOnAir(void)
{
    TVShow *show;

    IsRecordMixShowAlreadySpawned(TVSHOW_SECRET_BASE_VISIT, TRUE); // Delete old version of show
    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->secretBaseVisit.kind = TVSHOW_SECRET_BASE_VISIT;
        show->secretBaseVisit.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        StringCopy(show->secretBaseVisit.playerName, gSaveBlock2Ptr->playerName);
        SecretBaseVisit_CalculateDecorationData(show);
        SecretBaseVisit_CalculatePartyData(show);
        StorePlayerIdInRecordMixShow(show);
        show->secretBaseVisit.language = gGameLanguage;
    }
}

void TryPutBreakingNewsOnAir(void)
{
    TVShow *show;
    u8 i;
    u16 balls;

    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_BREAKING_NEWS, FALSE) != TRUE)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->breakingNews.kind = TVSHOW_BREAKING_NEWS;
        show->breakingNews.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        balls = 0;
        for (i = 0; i < POKEBALL_COUNT - 1; i++)
            balls += gBattleResults.catchAttempts[i];

        if (gBattleResults.usedMasterBall)
            balls++;
        show->breakingNews.location = gMapHeader.regionMapSectionId;
        StringCopy(show->breakingNews.playerName, gSaveBlock2Ptr->playerName);
        show->breakingNews.poke1Species = gBattleResults.playerMon1Species;
        switch (gBattleOutcome)
        {
        case B_OUTCOME_LOST:
        case B_OUTCOME_DREW:
            show->breakingNews.kind = TVSHOW_OFF_AIR;
            return;
        case B_OUTCOME_CAUGHT:
            show->breakingNews.outcome = 0;
            break;
        case B_OUTCOME_WON:
            show->breakingNews.outcome = 1;
            break;
        case B_OUTCOME_RAN:
        case B_OUTCOME_PLAYER_TELEPORTED:
        case B_OUTCOME_NO_SAFARI_BALLS:
            show->breakingNews.outcome = 2;
            break;
        case B_OUTCOME_MON_FLED:
        case B_OUTCOME_MON_TELEPORTED:
            show->breakingNews.outcome = 3;
            break;
        }
        show->breakingNews.lastOpponentSpecies = gBattleResults.lastOpponentSpecies;
        switch (show->breakingNews.outcome)
        {
        case 0:
            if (gBattleResults.usedMasterBall)
                show->breakingNews.caughtMonBall = ITEM_MASTER_BALL;
            else
                show->breakingNews.caughtMonBall = gBattleResults.caughtMonBall;
            show->breakingNews.balls = balls;
            break;
        case 1:
            show->breakingNews.lastUsedMove = gBattleResults.lastUsedMovePlayer;
            break;
        case 2:
        case 3:
            break;
        }
        StorePlayerIdInRecordMixShow(show);
        show->breakingNews.language = gGameLanguage;
    }
}

void TryPutLotteryWinnerReportOnAir(void)
{
    TVShow *show;

    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_LOTTO_WINNER, FALSE) != TRUE)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->lottoWinner.kind = TVSHOW_LOTTO_WINNER;
        show->lottoWinner.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        StringCopy(show->lottoWinner.playerName, gSaveBlock2Ptr->playerName);
        show->lottoWinner.whichPrize = 4 - gSpecialVar_0x8004;
        show->lottoWinner.item = gSpecialVar_0x8005;
        StorePlayerIdInRecordMixShow(show);
        show->lottoWinner.language = gGameLanguage;
    }
}

void TryPutBattleSeminarOnAir(u16 foeSpecies, u16 species, u8 moveIdx, const u16 *movePtr, u16 betterMove)
{
    TVShow *show;
    u8 i;
    u8 j;

    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_BATTLE_SEMINAR, FALSE) != TRUE)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->battleSeminar.kind = TVSHOW_BATTLE_SEMINAR;
        show->battleSeminar.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        StringCopy(show->battleSeminar.playerName, gSaveBlock2Ptr->playerName);
        show->battleSeminar.foeSpecies = foeSpecies;
        show->battleSeminar.species = species;
        show->battleSeminar.move = movePtr[moveIdx];
        for (i = 0, j = 0; i < MAX_MON_MOVES; i++)
        {
            if (i != moveIdx && movePtr[i])
            {
                show->battleSeminar.otherMoves[j] = movePtr[i];
                j++;
            }
        }
        show->battleSeminar.nOtherMoves = j;
        show->battleSeminar.betterMove = betterMove;
        StorePlayerIdInRecordMixShow(show);
        show->battleSeminar.language = gGameLanguage;
    }
}

void TryPutSafariFanClubOnAir(u8 monsCaught, u8 pokeblocksUsed)
{
    TVShow *show;

    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_SAFARI_FAN_CLUB, FALSE) != TRUE)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->safariFanClub.kind = TVSHOW_SAFARI_FAN_CLUB;
        show->safariFanClub.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        StringCopy(show->safariFanClub.playerName, gSaveBlock2Ptr->playerName);
        show->safariFanClub.monsCaught = monsCaught;
        show->safariFanClub.pokeblocksUsed = pokeblocksUsed;
        StorePlayerIdInRecordMixShow(show);
        show->safariFanClub.language = gGameLanguage;
    }
}

void TryPutSpotTheCutiesOnAir(struct Pokemon *pokemon, u8 ribbonMonDataIdx)
{
    TVShow *show;

    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_CUTIES, FALSE) != TRUE)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->cuties.kind = TVSHOW_CUTIES;
        show->cuties.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        StringCopy(show->cuties.playerName, gSaveBlock2Ptr->playerName);
        GetMonData(pokemon, MON_DATA_NICKNAME, show->cuties.nickname);
        StripExtCtrlCodes(show->cuties.nickname);
        show->cuties.nRibbons = GetRibbonCount(pokemon);
        show->cuties.selectedRibbon = MonDataIdxToRibbon(ribbonMonDataIdx);
        StorePlayerIdInRecordMixShow(show);
        show->cuties.language = gGameLanguage;
        if (show->cuties.language == LANGUAGE_JAPANESE || GetMonData(pokemon, MON_DATA_LANGUAGE) == LANGUAGE_JAPANESE)
            show->cuties.pokemonNameLanguage = LANGUAGE_JAPANESE;
        else
            show->cuties.pokemonNameLanguage = GetMonData(pokemon, MON_DATA_LANGUAGE);
    }
}

u8 GetRibbonCount(struct Pokemon *pokemon)
{
    u8 nRibbons;

    nRibbons = 0;
    nRibbons += GetMonData(pokemon, MON_DATA_COOL_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_BEAUTY_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_CUTE_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_SMART_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_TOUGH_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_CHAMPION_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_WINNING_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_VICTORY_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_ARTIST_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_EFFORT_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_MARINE_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_LAND_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_SKY_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_COUNTRY_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_NATIONAL_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_EARTH_RIBBON);
    nRibbons += GetMonData(pokemon, MON_DATA_WORLD_RIBBON);
    return nRibbons;
}

static u8 MonDataIdxToRibbon(u8 monDataIdx)
{
    if (monDataIdx == MON_DATA_CHAMPION_RIBBON) return CHAMPION_RIBBON;
    if (monDataIdx == MON_DATA_COOL_RIBBON)     return COOL_RIBBON_NORMAL;
    if (monDataIdx == MON_DATA_BEAUTY_RIBBON)   return BEAUTY_RIBBON_NORMAL;
    if (monDataIdx == MON_DATA_CUTE_RIBBON)     return CUTE_RIBBON_NORMAL;
    if (monDataIdx == MON_DATA_SMART_RIBBON)    return SMART_RIBBON_NORMAL;
    if (monDataIdx == MON_DATA_TOUGH_RIBBON)    return TOUGH_RIBBON_NORMAL;
    if (monDataIdx == MON_DATA_WINNING_RIBBON)  return WINNING_RIBBON;
    if (monDataIdx == MON_DATA_VICTORY_RIBBON)  return VICTORY_RIBBON;
    if (monDataIdx == MON_DATA_ARTIST_RIBBON)   return ARTIST_RIBBON;
    if (monDataIdx == MON_DATA_EFFORT_RIBBON)   return EFFORT_RIBBON;
    if (monDataIdx == MON_DATA_MARINE_RIBBON)   return MARINE_RIBBON;
    if (monDataIdx == MON_DATA_LAND_RIBBON)     return LAND_RIBBON;
    if (monDataIdx == MON_DATA_SKY_RIBBON)      return SKY_RIBBON;
    if (monDataIdx == MON_DATA_COUNTRY_RIBBON)  return COUNTRY_RIBBON;
    if (monDataIdx == MON_DATA_NATIONAL_RIBBON) return NATIONAL_RIBBON;
    if (monDataIdx == MON_DATA_EARTH_RIBBON)    return EARTH_RIBBON;
    if (monDataIdx == MON_DATA_WORLD_RIBBON)    return WORLD_RIBBON;
    return CHAMPION_RIBBON;
}

void TryPutTrainerFanClubOnAir(void)
{
    TVShow *show;

    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1 && IsRecordMixShowAlreadySpawned(TVSHOW_TRAINER_FAN_CLUB, FALSE) != TRUE)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->trainerFanClub.kind = TVSHOW_TRAINER_FAN_CLUB;
        show->trainerFanClub.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        StringCopy(show->trainerFanClub.playerName, gSaveBlock2Ptr->playerName);
        show->trainerFanClub.words[0] = gSaveBlock1Ptr->easyChatProfile[0];
        show->trainerFanClub.words[1] = gSaveBlock1Ptr->easyChatProfile[1];
        StorePlayerIdInRecordMixShow(show);
        show->trainerFanClub.language = gGameLanguage;
    }
}

bool8 ShouldHideFanClubInterviewer(void)
{
    sCurTVShowSlot = FindFirstEmptyNormalTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot == -1)
        return TRUE;

    TryReplaceOldTVShowOfKind(TVSHOW_FAN_CLUB_SPECIAL);
    if (gSpecialVar_Result == TRUE)
        return TRUE;

    if (gSaveBlock1Ptr->linkBattleRecords.entries[0].name[0] == EOS)
        return TRUE;

    return FALSE;
}

bool8 ShouldAirFrontierTVShow(void)
{
    u32 playerId;
    u8 showIdx;
    TVShow *shows;

    if (IsRecordMixShowAlreadySpawned(TVSHOW_FRONTIER, FALSE) == TRUE)
    {
        shows = gSaveBlock1Ptr->tvShows;
        playerId = GetPlayerIDAsU32();
        for (showIdx = NUM_NORMAL_TVSHOW_SLOTS; showIdx < LAST_TVSHOW_IDX; showIdx++)
        {
            if (shows[showIdx].common.kind == TVSHOW_FRONTIER && (playerId & 0xFF) == shows[showIdx].common.trainerIdLo && ((playerId >> 8) & 0xFF) == shows[showIdx].common.trainerIdHi)
            {
                DeleteTVShowInArrayByIdx(gSaveBlock1Ptr->tvShows, showIdx);
                CompactTVShowArray(gSaveBlock1Ptr->tvShows);
                return TRUE;
            }
        }
    }
    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot == -1)
        return FALSE;

    return TRUE;
}

void TryPutFrontierTVShowOnAir(u16 winStreak, u8 facilityAndMode)
{
    TVShow *show;

    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->frontier.kind = TVSHOW_FRONTIER;
        show->frontier.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        StringCopy(show->frontier.playerName, gSaveBlock2Ptr->playerName);
        show->frontier.winStreak = winStreak;
        show->frontier.facilityAndMode = facilityAndMode;
        switch (facilityAndMode)
        {
        case FRONTIER_SHOW_TOWER_SINGLES:
        case FRONTIER_SHOW_DOME_SINGLES:
        case FRONTIER_SHOW_DOME_DOUBLES:
        case FRONTIER_SHOW_FACTORY_SINGLES:
        case FRONTIER_SHOW_FACTORY_DOUBLES:
        case FRONTIER_SHOW_PIKE:
        case FRONTIER_SHOW_ARENA:
        case FRONTIER_SHOW_PALACE_SINGLES:
        case FRONTIER_SHOW_PALACE_DOUBLES:
        case FRONTIER_SHOW_PYRAMID:
            show->frontier.species1 = GetMonData(&gPlayerParty[0], MON_DATA_SPECIES, NULL);
            show->frontier.species2 = GetMonData(&gPlayerParty[1], MON_DATA_SPECIES, NULL);
            show->frontier.species3 = GetMonData(&gPlayerParty[2], MON_DATA_SPECIES, NULL);
            break;
        case FRONTIER_SHOW_TOWER_DOUBLES:
            show->frontier.species1 = GetMonData(&gPlayerParty[0], MON_DATA_SPECIES, NULL);
            show->frontier.species2 = GetMonData(&gPlayerParty[1], MON_DATA_SPECIES, NULL);
            show->frontier.species3 = GetMonData(&gPlayerParty[2], MON_DATA_SPECIES, NULL);
            show->frontier.species4 = GetMonData(&gPlayerParty[3], MON_DATA_SPECIES, NULL);
            break;
        case FRONTIER_SHOW_TOWER_MULTIS:
            show->frontier.species1 = GetMonData(&gPlayerParty[0], MON_DATA_SPECIES, NULL);
            show->frontier.species2 = GetMonData(&gPlayerParty[1], MON_DATA_SPECIES, NULL);
            break;
        case FRONTIER_SHOW_TOWER_LINK_MULTIS:
            show->frontier.species1 = GetMonData(&gSaveBlock1Ptr->playerParty[gSaveBlock2Ptr->frontier.selectedPartyMons[0] - 1], MON_DATA_SPECIES, NULL);
            show->frontier.species2 = GetMonData(&gSaveBlock1Ptr->playerParty[gSaveBlock2Ptr->frontier.selectedPartyMons[1] - 1], MON_DATA_SPECIES, NULL);
            break;
        }
        StorePlayerIdInRecordMixShow(show);
        show->frontier.language = gGameLanguage;
    }
}

void TryPutSecretBaseSecretsOnAir(void)
{
    TVShow *show;
    u8 strbuf[32];

    if (IsRecordMixShowAlreadySpawned(TVSHOW_SECRET_BASE_SECRETS, FALSE) != TRUE)
    {
        sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
        if (sCurTVShowSlot != -1)
        {
            show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
            show->secretBaseSecrets.kind = TVSHOW_SECRET_BASE_SECRETS;
            show->secretBaseSecrets.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
            StringCopy(show->secretBaseSecrets.playerName, gSaveBlock2Ptr->playerName);
            show->secretBaseSecrets.stepsInBase = VarGet(VAR_SECRET_BASE_STEP_COUNTER);
            CopyCurSecretBaseOwnerName_StrVar1();
            StringCopy(strbuf, gStringVar1);
            StripExtCtrlCodes(strbuf);
            StringCopy(show->secretBaseSecrets.baseOwnersName, strbuf);
            show->secretBaseSecrets.item = VarGet(VAR_SECRET_BASE_LAST_ITEM_USED);
            show->secretBaseSecrets.flags = VarGet(VAR_SECRET_BASE_LOW_TV_FLAGS) + (VarGet(VAR_SECRET_BASE_HIGH_TV_FLAGS) << 16);
            StorePlayerIdInRecordMixShow(show);
            show->secretBaseSecrets.language = gGameLanguage;
            if (show->secretBaseSecrets.language == LANGUAGE_JAPANESE || gSaveBlock1Ptr->secretBases[VarGet(VAR_CURRENT_SECRET_BASE)].language == LANGUAGE_JAPANESE)
                show->secretBaseSecrets.baseOwnersNameLanguage = LANGUAGE_JAPANESE;
            else
                show->secretBaseSecrets.baseOwnersNameLanguage = gSaveBlock1Ptr->secretBases[VarGet(VAR_CURRENT_SECRET_BASE)].language;
        }
    }
}

// Check var thresholds required to trigger the Number One show
// The vars are reset afterwards regardless
static void ResolveNumberOneShow(u16 days)
{
    u8 i;

    for (i = 0; i < ARRAY_COUNT(sNumberOneVarsAndThresholds); i++)
    {
        if (VarGet(sNumberOneVarsAndThresholds[i][0]) >= sNumberOneVarsAndThresholds[i][1])
        {
            TryPutNumberOneOnAir(i);
            break;
        }
    }

    for (i = 0; i < ARRAY_COUNT(sNumberOneVarsAndThresholds); i++)
        VarSet(sNumberOneVarsAndThresholds[i][0], 0);
}

static void TryPutNumberOneOnAir(u8 actionIdx)
{
    TVShow *show;

    IsRecordMixShowAlreadySpawned(TVSHOW_NUMBER_ONE, TRUE); // Delete old version of show
    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(gSaveBlock1Ptr->tvShows);
    if (sCurTVShowSlot != -1)
    {
        show = &gSaveBlock1Ptr->tvShows[sCurTVShowSlot];
        show->numberOne.kind = TVSHOW_NUMBER_ONE;
        show->numberOne.active = FALSE; // NOTE: Show is not active until passed via Record Mix.
        StringCopy(show->numberOne.playerName, gSaveBlock2Ptr->playerName);
        show->numberOne.actionIdx = actionIdx;
        show->numberOne.count = VarGet(sNumberOneVarsAndThresholds[actionIdx][0]);
        StorePlayerIdInRecordMixShow(show);
        show->numberOne.language = gGameLanguage;
    }
}

void IncrementDailySlotsUses(void)
{
    VarSet(VAR_DAILY_SLOTS, VarGet(VAR_DAILY_SLOTS) + 1);
}

void IncrementDailyRouletteUses(void)
{
    VarSet(VAR_DAILY_ROULETTE, VarGet(VAR_DAILY_ROULETTE) + 1);
}

void IncrementDailyWildBattles(void)
{
    VarSet(VAR_DAILY_WILDS, VarGet(VAR_DAILY_WILDS) + 1);
}

void IncrementDailyBerryBlender(void)
{
    VarSet(VAR_DAILY_BLENDER, VarGet(VAR_DAILY_BLENDER) + 1);
}

void IncrementDailyPlantedBerries(void)
{
    VarSet(VAR_DAILY_PLANTED_BERRIES, VarGet(VAR_DAILY_PLANTED_BERRIES) + 1);
}

void IncrementDailyPickedBerries(void)
{
    VarSet(VAR_DAILY_PICKED_BERRIES, VarGet(VAR_DAILY_PICKED_BERRIES) + gSpecialVar_0x8006);
}

void IncrementDailyBattlePoints(u16 delta)
{
    VarSet(VAR_DAILY_BP, VarGet(VAR_DAILY_BP) + delta);
}

// PokeNews

static void TryPutRandomPokeNewsOnAir(void)
{
    if (FlagGet(FLAG_SYS_GAME_CLEAR))
    {
        sCurTVShowSlot = GetFirstEmptyPokeNewsSlot(gSaveBlock1Ptr->pokeNews);
        if (sCurTVShowSlot != -1 && rbernoulli(1, 100) != TRUE)
        {
            u8 newsKind = (Random() % NUM_POKENEWS_TYPES) + 1; // +1 to skip over POKENEWS_NONE
            if (IsAddingPokeNewsDisallowed(newsKind) != TRUE)
            {
                gSaveBlock1Ptr->pokeNews[sCurTVShowSlot].kind = newsKind;
                gSaveBlock1Ptr->pokeNews[sCurTVShowSlot].dayCountdown = POKENEWS_COUNTDOWN;
                gSaveBlock1Ptr->pokeNews[sCurTVShowSlot].state = POKENEWS_STATE_UPCOMING;
            }
        }
    }
}

static s8 GetFirstEmptyPokeNewsSlot(PokeNews *pokeNews)
{
    s8 i;

    for (i = 0; i < POKE_NEWS_COUNT; i++)
    {
        if (pokeNews[i].kind == POKENEWS_NONE)
            return i;
    }
    return -1;
}

static void ClearPokeNews(void)
{
    u8 i;

    for (i = 0; i < POKE_NEWS_COUNT; i++)
        ClearPokeNewsBySlot(i);
}

static void ClearPokeNewsBySlot(u8 i)
{
    gSaveBlock1Ptr->pokeNews[i].kind = POKENEWS_NONE;
    gSaveBlock1Ptr->pokeNews[i].state = POKENEWS_STATE_INACTIVE;
    gSaveBlock1Ptr->pokeNews[i].dayCountdown = 0;
}

static void CompactPokeNews(void)
{
    u8 i;
    u8 j;

    for (i = 0; i < POKE_NEWS_COUNT - 1; i++)
    {
        if (gSaveBlock1Ptr->pokeNews[i].kind == POKENEWS_NONE)
        {
            for (j = i + 1; j < POKE_NEWS_COUNT; j++)
            {
                if (gSaveBlock1Ptr->pokeNews[j].kind != POKENEWS_NONE)
                {
                    gSaveBlock1Ptr->pokeNews[i] = gSaveBlock1Ptr->pokeNews[j];
                    ClearPokeNewsBySlot(j);
                    break;
                }
            }
        }
    }
}

static u8 FindAnyPokeNewsOnTheAir(void)
{
    u8 i;

    for (i = 0; i < POKE_NEWS_COUNT; i++)
    {
        if (gSaveBlock1Ptr->pokeNews[i].kind != POKENEWS_NONE
         && gSaveBlock1Ptr->pokeNews[i].state == POKENEWS_STATE_UPCOMING
         && gSaveBlock1Ptr->pokeNews[i].dayCountdown < POKENEWS_COUNTDOWN - 1)
            return i;
    }
    return 0xFF;
}

void DoPokeNews(void)
{
    u8 i = FindAnyPokeNewsOnTheAir();
    if (i == 0xFF)
    {
        gSpecialVar_Result = FALSE;
    }
    else
    {
        if (gSaveBlock1Ptr->pokeNews[i].dayCountdown == 0)
        {
            // News event is occurring, make comment depending on how much time is left
            gSaveBlock1Ptr->pokeNews[i].state = POKENEWS_STATE_ACTIVE;
            if (gLocalTime.hours < 20)
                ShowFieldMessage(sPokeNewsTextGroup_Ongoing[gSaveBlock1Ptr->pokeNews[i].kind]);
            else
                ShowFieldMessage(sPokeNewsTextGroup_Ending[gSaveBlock1Ptr->pokeNews[i].kind]);
        }
        else
        {
            // News event is upcoming, make comment about countdown to event
            u16 dayCountdown = gSaveBlock1Ptr->pokeNews[i].dayCountdown;
            ConvertIntToDecimalStringN(gStringVar1, dayCountdown, STR_CONV_MODE_LEFT_ALIGN, 1);
            
            // Mark as inactive so the countdown TV airing doesn't repeat
            // Will be flagged as "upcoming" again by UpdatePokeNewsCountdown
            gSaveBlock1Ptr->pokeNews[i].state = POKENEWS_STATE_INACTIVE;
            ShowFieldMessage(sPokeNewsTextGroup_Upcoming[gSaveBlock1Ptr->pokeNews[i].kind]);
        }
        gSpecialVar_Result = TRUE;
    }
}

bool8 IsPokeNewsActive(u8 newsKind)
{
    u8 i;

    if (newsKind == POKENEWS_NONE)
        return FALSE;

    for (i = 0; i < POKE_NEWS_COUNT; i++)
    {
        if (gSaveBlock1Ptr->pokeNews[i].kind == newsKind)
        {
            if (gSaveBlock1Ptr->pokeNews[i].state == POKENEWS_STATE_ACTIVE && ShouldApplyPokeNewsEffect(newsKind))
                return TRUE;

            return FALSE;
        }
    }
    return FALSE;
}

// Returns TRUE if the effects of the given PokeNews should be applied.
// For POKENEWS_SLATEPORT / POKENEWS_LILYCOVE, only apply the effect if
// the player is talking to the Energy Guru / at the Dept Store Rooftop.
// For any other type of PokeNews this is always TRUE.
static bool8 ShouldApplyPokeNewsEffect(u8 newsKind)
{
    switch (newsKind)
    {
    case POKENEWS_SLATEPORT:
        if (gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(SLATEPORT_CITY)
         && gSaveBlock1Ptr->location.mapNum == MAP_NUM(SLATEPORT_CITY)
         && gSpecialVar_LastTalked == LOCALID_SLATEPORT_ENERGY_GURU)
            return TRUE;
        return FALSE;
    case POKENEWS_LILYCOVE:
        if (gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP)
         && gSaveBlock1Ptr->location.mapNum == MAP_NUM(LILYCOVE_CITY_DEPARTMENT_STORE_ROOFTOP))
            return TRUE;
        return FALSE;
    }
    return TRUE;
}

static bool8 IsAddingPokeNewsDisallowed(u8 newsKind)
{
    u8 i;
    if (newsKind == POKENEWS_NONE)
        return TRUE;

    // Check if this type of news is already active
    for (i = 0; i < POKE_NEWS_COUNT; i++)
    {
        if (gSaveBlock1Ptr->pokeNews[i].kind == newsKind)
            return TRUE;
    }
    return FALSE;
}

static void UpdatePokeNewsCountdown(u16 days)
{
    u8 i;

    for (i = 0; i < POKE_NEWS_COUNT; i++)
    {
        if (gSaveBlock1Ptr->pokeNews[i].kind != POKENEWS_NONE)
        {
            if (gSaveBlock1Ptr->pokeNews[i].dayCountdown < days)
            {
                // News event has elapsed, clear it from list
                ClearPokeNewsBySlot(i);
            }
            else
            {
                // Progress countdown to news event
                if (gSaveBlock1Ptr->pokeNews[i].state == POKENEWS_STATE_INACTIVE && FlagGet(FLAG_SYS_GAME_CLEAR) == TRUE)
                    gSaveBlock1Ptr->pokeNews[i].state = POKENEWS_STATE_UPCOMING;

                gSaveBlock1Ptr->pokeNews[i].dayCountdown -= days;
            }
        }
    }
    CompactPokeNews();
}

void CopyContestRankToStringVar(u8 varIdx, u8 rank)
{
    switch (rank)
    {
    case CONTEST_RANK_NORMAL:
        StringCopy(gTVStringVarPtrs[varIdx], gStdStrings[STDSTRING_NORMAL]);
        break;
    case CONTEST_RANK_SUPER:
        StringCopy(gTVStringVarPtrs[varIdx], gStdStrings[STDSTRING_SUPER]);
        break;
    case CONTEST_RANK_HYPER:
        StringCopy(gTVStringVarPtrs[varIdx], gStdStrings[STDSTRING_HYPER]);
        break;
    case CONTEST_RANK_MASTER:
        StringCopy(gTVStringVarPtrs[varIdx], gStdStrings[STDSTRING_MASTER]);
        break;
    }
}

void CopyContestCategoryToStringVar(u8 varIdx, u8 category)
{
    switch (category)
    {
    case CONTEST_CATEGORY_COOL:
        StringCopy(gTVStringVarPtrs[varIdx], gStdStrings[STDSTRING_COOL]);
        break;
    case CONTEST_CATEGORY_BEAUTY:
        StringCopy(gTVStringVarPtrs[varIdx], gStdStrings[STDSTRING_BEAUTY]);
        break;
    case CONTEST_CATEGORY_CUTE:
        StringCopy(gTVStringVarPtrs[varIdx], gStdStrings[STDSTRING_CUTE]);
        break;
    case CONTEST_CATEGORY_SMART:
        StringCopy(gTVStringVarPtrs[varIdx], gStdStrings[STDSTRING_SMART]);
        break;
    case CONTEST_CATEGORY_TOUGH:
        StringCopy(gTVStringVarPtrs[varIdx], gStdStrings[STDSTRING_TOUGH]);
        break;
    }
}

void SetContestCategoryStringVarForInterview(void)
{
    TVShow *show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    CopyContestCategoryToStringVar(1, show->bravoTrainer.contestCategory);
}

void ConvertIntToDecimalString(u8 varIdx, int value)
{
    int nDigits = CountDigits(value);
    ConvertIntToDecimalStringN(gTVStringVarPtrs[varIdx], value, STR_CONV_MODE_LEFT_ALIGN, nDigits);
}

size_t CountDigits(int value)
{
    if (value / 10 == 0)        return 1;
    if (value / 100 == 0)       return 2;
    if (value / 1000 == 0)      return 3;
    if (value / 10000 == 0)     return 4;
    if (value / 100000 == 0)    return 5;
    if (value / 1000000 == 0)   return 6;
    if (value / 10000000 == 0)  return 7;
    if (value / 100000000 == 0) return 8;

    return 1;
}

static void SmartShopper_BufferPurchaseTotal(u8 varIdx, TVShow *show)
{
    u8 i;
    int price = 0;
    for (i = 0; i < SMARTSHOPPER_NUM_ITEMS; i++)
    {
        if (show->smartshopperShow.itemIds[i] != ITEM_NONE)
            price += ItemId_GetPrice(show->smartshopperShow.itemIds[i]) * show->smartshopperShow.itemAmounts[i];
    }

    if (show->smartshopperShow.priceReduced == TRUE)
        ConvertIntToDecimalString(varIdx, price >> 1);
    else
        ConvertIntToDecimalString(varIdx, price);
}

static bool8 IsRecordMixShowAlreadySpawned(u8 kind, bool8 delete)
{
    u32 playerId;
    TVShow *shows;
    u8 i;

    shows = gSaveBlock1Ptr->tvShows;
    playerId = GetPlayerIDAsU32();
    for (i = NUM_NORMAL_TVSHOW_SLOTS; i < LAST_TVSHOW_IDX; i++)
    {
        if (shows[i].common.kind == kind
         && (playerId & 0xFF) == shows[i].common.trainerIdLo
         && ((playerId >> 8) & 0xFF) == shows[i].common.trainerIdHi)
        {
            if (delete == TRUE)
            {
                DeleteTVShowInArrayByIdx(gSaveBlock1Ptr->tvShows, i);
                CompactTVShowArray(gSaveBlock1Ptr->tvShows);
            }
            return TRUE;
        }
    }
    return FALSE;
}

static void SortPurchasesByQuantity(void)
{
    u8 i, j;
    
    for (i = 0; i < SMARTSHOPPER_NUM_ITEMS - 1; i++)
    {
        for (j = i + 1; j < SMARTSHOPPER_NUM_ITEMS; j++)
        {
            if (gMartPurchaseHistory[i].quantity < gMartPurchaseHistory[j].quantity)
            {
                u16 tempItemId = gMartPurchaseHistory[i].itemId;
                u16 tempQuantity = gMartPurchaseHistory[i].quantity;
                gMartPurchaseHistory[i].itemId = gMartPurchaseHistory[j].itemId;
                gMartPurchaseHistory[i].quantity = gMartPurchaseHistory[j].quantity;
                gMartPurchaseHistory[j].itemId = tempItemId;
                gMartPurchaseHistory[j].quantity = tempQuantity;
            }
        }
    }
}

static void TryReplaceOldTVShowOfKind(u8 kind)
{
    u8 i;
    for (i = 0; i < NUM_NORMAL_TVSHOW_SLOTS; i++)
    {
        if (gSaveBlock1Ptr->tvShows[i].common.kind == kind)
        {
            if (gSaveBlock1Ptr->tvShows[i].common.active == TRUE)
            {
                // Old TV show is still active, don't replace
                gSpecialVar_Result = TRUE;
            }
            else
            {
                // Old TV show is inactive, replace it and get new slot
                DeleteTVShowInArrayByIdx(gSaveBlock1Ptr->tvShows, i);
                CompactTVShowArray(gSaveBlock1Ptr->tvShows);
                Script_FindFirstEmptyNormalTVShowSlot();
            }
            return;
        }
    }

    // Old TV show doesn't exist, just get new slot
    Script_FindFirstEmptyNormalTVShowSlot();
}

void InterviewBefore(void)
{
    gSpecialVar_Result = FALSE;
    switch (gSpecialVar_0x8005)
    {
    case TVSHOW_FAN_CLUB_LETTER:
        InterviewBefore_FanClubLetter();
        break;
    case TVSHOW_RECENT_HAPPENINGS:
        InterviewBefore_RecentHappenings();
        break;
    case TVSHOW_PKMN_FAN_CLUB_OPINIONS:
        InterviewBefore_PkmnFanClubOpinions();
        break;
    case TVSHOW_DUMMY:
        InterviewBefore_Dummy();
        break;
    case TVSHOW_NAME_RATER_SHOW:
        InterviewBefore_NameRater();
        break;
    case TVSHOW_BRAVO_TRAINER_POKEMON_PROFILE:
        InterviewBefore_BravoTrainerPkmnProfile();
        break;
    case TVSHOW_BRAVO_TRAINER_BATTLE_TOWER_PROFILE:
        InterviewBefore_BravoTrainerBTProfile();
        break;
    case TVSHOW_CONTEST_LIVE_UPDATES:
        InterviewBefore_ContestLiveUpdates();
        break;
    case TVSHOW_3_CHEERS_FOR_POKEBLOCKS:
        InterviewBefore_3CheersForPokeblocks();
        break;
    case TVSHOW_FAN_CLUB_SPECIAL:
        InterviewBefore_FanClubSpecial();
        break;
    }
}

static void InterviewBefore_FanClubLetter(void)
{
    TryReplaceOldTVShowOfKind(TVSHOW_FAN_CLUB_LETTER);
    if (!gSpecialVar_Result)
    {
        StringCopy(gStringVar1, gSpeciesNames[GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_SPECIES, NULL)]);
        InitializeEasyChatWordArray(gSaveBlock1Ptr->tvShows[sCurTVShowSlot].fanclubLetter.words,
                        ARRAY_COUNT(gSaveBlock1Ptr->tvShows[sCurTVShowSlot].fanclubLetter.words));
    }
}

static void InterviewBefore_RecentHappenings(void)
{
    TryReplaceOldTVShowOfKind(TVSHOW_RECENT_HAPPENINGS);
    if (!gSpecialVar_Result)
    {
        InitializeEasyChatWordArray(gSaveBlock1Ptr->tvShows[sCurTVShowSlot].recentHappenings.words,
                        ARRAY_COUNT(gSaveBlock1Ptr->tvShows[sCurTVShowSlot].recentHappenings.words));
    }
}

static void InterviewBefore_PkmnFanClubOpinions(void)
{
    TryReplaceOldTVShowOfKind(TVSHOW_PKMN_FAN_CLUB_OPINIONS);
    if (!gSpecialVar_Result)
    {
        StringCopy(gStringVar1, gSpeciesNames[GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_SPECIES, NULL)]);
        GetMonData(&gPlayerParty[GetLeadMonIndex()], MON_DATA_NICKNAME, gStringVar2);
        StringGet_Nickname(gStringVar2);
        InitializeEasyChatWordArray(gSaveBlock1Ptr->tvShows[sCurTVShowSlot].fanclubOpinions.words,
                        ARRAY_COUNT(gSaveBlock1Ptr->tvShows[sCurTVShowSlot].fanclubOpinions.words));
    }
}

static void InterviewBefore_Dummy(void)
{
    gSpecialVar_Result = TRUE;
}

static void InterviewBefore_NameRater(void)
{
    TryReplaceOldTVShowOfKind(TVSHOW_NAME_RATER_SHOW);
}

static void InterviewBefore_BravoTrainerPkmnProfile(void)
{
    TryReplaceOldTVShowOfKind(TVSHOW_BRAVO_TRAINER_POKEMON_PROFILE);
    if (!gSpecialVar_Result)
        InitializeEasyChatWordArray(gSaveBlock1Ptr->tvShows[sCurTVShowSlot].bravoTrainer.words,
                        ARRAY_COUNT(gSaveBlock1Ptr->tvShows[sCurTVShowSlot].bravoTrainer.words));
}

static void InterviewBefore_ContestLiveUpdates(void)
{
    TryReplaceOldTVShowOfKind(TVSHOW_CONTEST_LIVE_UPDATES);
}

static void InterviewBefore_3CheersForPokeblocks(void)
{
    TryReplaceOldTVShowOfKind(TVSHOW_3_CHEERS_FOR_POKEBLOCKS);
}

static void InterviewBefore_BravoTrainerBTProfile(void)
{
    TryReplaceOldTVShowOfKind(TVSHOW_BRAVO_TRAINER_BATTLE_TOWER_PROFILE);
    if (!gSpecialVar_Result)
        InitializeEasyChatWordArray(gSaveBlock1Ptr->tvShows[sCurTVShowSlot].bravoTrainerTower.words,
                        ARRAY_COUNT(gSaveBlock1Ptr->tvShows[sCurTVShowSlot].bravoTrainerTower.words));
}

static void InterviewBefore_FanClubSpecial(void)
{
    TryReplaceOldTVShowOfKind(TVSHOW_FAN_CLUB_SPECIAL);
    if (!gSpecialVar_Result)
        InitializeEasyChatWordArray(gSaveBlock1Ptr->tvShows[sCurTVShowSlot].fanClubSpecial.words,
                        ARRAY_COUNT(gSaveBlock1Ptr->tvShows[sCurTVShowSlot].fanClubSpecial.words));
}

static bool8 IsPartyMonNicknamedOrNotEnglish(u8 monIdx)
{
    struct Pokemon *pokemon;
    u8 language;

    pokemon = &gPlayerParty[monIdx];
    GetMonData(pokemon, MON_DATA_NICKNAME, gStringVar1);
    language = GetMonData(pokemon, MON_DATA_LANGUAGE, &language);
    if (language == GAME_LANGUAGE && !StringCompare(gSpeciesNames[GetMonData(pokemon, MON_DATA_SPECIES, NULL)], gStringVar1))
        return FALSE;

    return TRUE;
}

bool8 IsLeadMonNicknamedOrNotEnglish(void)
{
    return IsPartyMonNicknamedOrNotEnglish(GetLeadMonIndex());
}

static void DeleteTVShowInArrayByIdx(TVShow *shows, u8 idx)
{
    u8 i;

    shows[idx].commonInit.kind = TVSHOW_OFF_AIR;
    shows[idx].commonInit.active = FALSE;
    for (i = 0; i < ARRAY_COUNT(shows[idx].commonInit.data); i++)
        shows[idx].commonInit.data[i] = 0;
}

static void CompactTVShowArray(TVShow *shows)
{
    u8 i;
    u8 j;

    // Compact normal TV shows
    for (i = 0; i < NUM_NORMAL_TVSHOW_SLOTS - 1; i++)
    {
        if (shows[i].common.kind == TVSHOW_OFF_AIR)
        {
            for (j = i + 1; j < NUM_NORMAL_TVSHOW_SLOTS; j++)
            {
                if (shows[j].common.kind != TVSHOW_OFF_AIR)
                {
                    shows[i] = shows[j];
                    DeleteTVShowInArrayByIdx(shows, j);
                    break;
                }
            }
        }
    }

    // Compact Record Mix TV shows
    for (i = NUM_NORMAL_TVSHOW_SLOTS; i < LAST_TVSHOW_IDX; i++)
    {
        if (shows[i].common.kind == TVSHOW_OFF_AIR)
        {
            for (j = i + 1; j < LAST_TVSHOW_IDX; j++)
            {
                if (shows[j].common.kind != TVSHOW_OFF_AIR)
                {
                    shows[i] = shows[j];
                    DeleteTVShowInArrayByIdx(shows, j);
                    break;
                }
            }
        }
    }
}

static u16 GetRandomDifferentSpeciesAndNameSeenByPlayer(u8 varIdx, u16 excludedSpecies)
{
    u16 species = GetRandomDifferentSpeciesSeenByPlayer(excludedSpecies);
    StringCopy(gTVStringVarPtrs[varIdx], gSpeciesNames[species]);
    return species;
}

static u16 GetRandomDifferentSpeciesSeenByPlayer(u16 excludedSpecies)
{
    u16 species = Random() % (NUM_SPECIES - 1) + 1;
    u16 initSpecies = species;

    while (GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_SEEN) != TRUE || species == excludedSpecies)
    {
        if (species == SPECIES_NONE + 1)
            species = NUM_SPECIES - 1;
        else
            species--;

        if (species == initSpecies)
        {
            // Looped back to initial species (only Pokémon seen), must choose excluded species
            species = excludedSpecies;
            return species;
        }
    };
    return species;
}

static void Script_FindFirstEmptyNormalTVShowSlot(void)
{
    sCurTVShowSlot = FindFirstEmptyNormalTVShowSlot(gSaveBlock1Ptr->tvShows);
    gSpecialVar_0x8006 = sCurTVShowSlot;
    if (sCurTVShowSlot == -1)
        gSpecialVar_Result = TRUE;  // Failed to find empty slot
    else
        gSpecialVar_Result = FALSE; // Found empty slot
}

static s8 FindFirstEmptyNormalTVShowSlot(TVShow *shows)
{
    u8 i;

    for (i = 0; i < NUM_NORMAL_TVSHOW_SLOTS; i++)
    {
        if (shows[i].common.kind == TVSHOW_OFF_AIR)
            return i;
    }
    return -1;
}

static s8 FindFirstEmptyRecordMixTVShowSlot(TVShow *shows)
{
    s8 i;

    for (i = NUM_NORMAL_TVSHOW_SLOTS; i < LAST_TVSHOW_IDX; i++)
    {
        if (shows[i].common.kind == TVSHOW_OFF_AIR)
            return i;
    }
    return -1;
}

static bool8 BernoulliTrial(u16 ratio)
{
    if (Random() <= ratio)
        return FALSE;

    return TRUE;
}

// For TVSHOW_FAN_CLUB_LETTER / TVSHOW_RECENT_HAPPENINGS
// Both are assumed to have the same struct layout
static void GetRandomWordFromShow(TVShow *show)
{
    u8 i;

    i = Random() % ARRAY_COUNT(show->fanclubLetter.words);

    // From random point, get first non-empty word
    while (TRUE)
    {
        if (i == ARRAY_COUNT(show->fanclubLetter.words))
            i = 0;

        if (show->fanclubLetter.words[i] != EC_EMPTY_WORD)
            break;
        i++;
    }
    CopyEasyChatWord(gStringVar3, show->fanclubLetter.words[i]);
}

static u8 GetRandomNameRaterStateFromName(TVShow *show)
{
    u8 i;
    u16 nameSum;

    nameSum = 0;
    for (i = 0; i < POKEMON_NAME_LENGTH + 1; i++)
    {
        if (show->nameRaterShow.pokemonName[i] == EOS)
            break;

        nameSum += show->nameRaterShow.pokemonName[i];
    }
    return nameSum & 7;
}

static void GetNicknameSubstring(u8 varIdx, u8 whichPosition, u8 charParam, u16 whichString, u16 species, TVShow *show)
{
    u8 buff[16];
    u8 i;
    u16 strlen;

    for (i = 0; i < 3; i++)
        buff[i] = EOS;

    if (whichString == 0)
    {
        strlen = StringLength(show->nameRaterShow.trainerName);
        if (charParam == 0)
        {
            buff[0] = show->nameRaterShow.trainerName[whichPosition];
        }
        else if (charParam == 1)
        {
            buff[0] = show->nameRaterShow.trainerName[strlen - whichPosition];
        }
        else if (charParam == 2)
        {
            buff[0] = show->nameRaterShow.trainerName[whichPosition];
            buff[1] = show->nameRaterShow.trainerName[whichPosition + 1];
        }
        else
        {
            buff[0] = show->nameRaterShow.trainerName[strlen - (whichPosition + 2)];
            buff[1] = show->nameRaterShow.trainerName[strlen - (whichPosition + 1)];
        }
        ConvertInternationalString(buff, show->nameRaterShow.language);
    }
    else if (whichString == 1)
    {
        strlen = StringLength(show->nameRaterShow.pokemonName);
        if (charParam == 0)
        {
            buff[0] = show->nameRaterShow.pokemonName[whichPosition];
        }
        else if (charParam == 1)
        {
            buff[0] = show->nameRaterShow.pokemonName[strlen - whichPosition];
        }
        else if (charParam == 2)
        {
            buff[0] = show->nameRaterShow.pokemonName[whichPosition];
            buff[1] = show->nameRaterShow.pokemonName[whichPosition + 1];
        }
        else
        {
            buff[0] = show->nameRaterShow.pokemonName[strlen - (whichPosition + 2)];
            buff[1] = show->nameRaterShow.pokemonName[strlen - (whichPosition + 1)];
        }
        ConvertInternationalString(buff, show->nameRaterShow.pokemonNameLanguage);
    }
    else
    {
        strlen = StringLength(gSpeciesNames[species]);
        if (charParam == 0)
        {
            buff[0] = gSpeciesNames[species][whichPosition];
        }
        else if (charParam == 1)
        {
            buff[0] = gSpeciesNames[species][strlen - whichPosition];
        }
        else if (charParam == 2)
        {
            buff[0] = gSpeciesNames[species][whichPosition];
            buff[1] = gSpeciesNames[species][whichPosition + 1];
        }
        else
        {
            buff[0] = gSpeciesNames[species][strlen - (whichPosition + 2)];
            buff[1] = gSpeciesNames[species][strlen - (whichPosition + 1)];
        }
    }
    StringCopy(gTVStringVarPtrs[varIdx], buff);
}

// Unused script special
bool8 IsTVShowAlreadyInQueue(void)
{
    u8 i;

    for (i = 0; i < NUM_NORMAL_TVSHOW_SLOTS; i++)
    {
        if (gSaveBlock1Ptr->tvShows[i].common.kind == gSpecialVar_0x8004)
            return TRUE;
    }
    return FALSE;
}

bool8 TryPutNameRaterShowOnTheAir(void)
{
    GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_NICKNAME, gStringVar1);

    // Nickname wasnt changed
    if (!StringCompare(gStringVar3, gStringVar1))
        return FALSE;

    PutNameRaterShowOnTheAir();
    return TRUE;
}

void ChangePokemonNickname(void)
{
    void ChangePokemonNickname_CB(void);

    GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_NICKNAME, gStringVar3);
    GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_NICKNAME, gStringVar2);
    DoNamingScreen(NAMING_SCREEN_NICKNAME, gStringVar2, GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_SPECIES, NULL), GetMonGender(&gPlayerParty[gSpecialVar_0x8004]), GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_PERSONALITY, NULL), ChangePokemonNickname_CB);
}

void ChangePokemonNickname_CB(void)
{
    SetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_NICKNAME, gStringVar2);
    CB2_ReturnToFieldContinueScriptPlayMapMusic();
}

void ChangeBoxPokemonNickname(void)
{
    struct BoxPokemon *boxMon;

    boxMon = GetBoxedMonPtr(gSpecialVar_MonBoxId, gSpecialVar_MonBoxPos);
    GetBoxMonData(boxMon, MON_DATA_NICKNAME, gStringVar3);
    GetBoxMonData(boxMon, MON_DATA_NICKNAME, gStringVar2);
    DoNamingScreen(NAMING_SCREEN_NICKNAME, gStringVar2, GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL), GetBoxMonGender(boxMon), GetBoxMonData(boxMon, MON_DATA_PERSONALITY, NULL), ChangeBoxPokemonNickname_CB);
}

static void ChangeBoxPokemonNickname_CB(void)
{
    SetBoxMonNickAt(gSpecialVar_MonBoxId, gSpecialVar_MonBoxPos, gStringVar2);
    CB2_ReturnToFieldContinueScriptPlayMapMusic();
}

void BufferMonNickname(void)
{
    GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_NICKNAME, gStringVar1);
    StringGet_Nickname(gStringVar1);
}

void IsMonOTIDNotPlayers(void)
{
    if (GetPlayerIDAsU32() == GetMonData(&gPlayerParty[gSpecialVar_0x8004], MON_DATA_OT_ID, NULL))
        gSpecialVar_Result = FALSE;
    else
        gSpecialVar_Result = TRUE;
}

static u8 GetTVGroupByShowId(u8 kind)
{
    if (kind == TVSHOW_OFF_AIR)
        return TVGROUP_NONE;

    if (kind >= TVGROUP_NORMAL_START && kind <= TVGROUP_NORMAL_END)
        return TVGROUP_NORMAL;

    if (kind >= TVGROUP_RECORD_MIX_START && kind <= TVGROUP_RECORD_MIX_END)
        return TVGROUP_RECORD_MIX;

    if (kind >= TVGROUP_OUTBREAK_START && kind <= TVGROUP_OUTBREAK_END)
        return TVGROUP_OUTBREAK;

    return TVGROUP_NONE;
}

u32 GetPlayerIDAsU32(void)
{
    return (gSaveBlock2Ptr->playerTrainerId[3] << 24) | (gSaveBlock2Ptr->playerTrainerId[2] << 16) | (gSaveBlock2Ptr->playerTrainerId[1] << 8) | gSaveBlock2Ptr->playerTrainerId[0];
}

u8 CheckForPlayersHouseNews(void)
{
    // Check if not in Littleroot house map group
    if (gSaveBlock1Ptr->location.mapGroup != MAP_GROUP(LITTLEROOT_TOWN_BRENDANS_HOUSE_1F))
        return PLAYERS_HOUSE_TV_NONE;

    // Check if not in player's house (dependent on gender)
    if (gSaveBlock2Ptr->playerGender == MALE)
    {
        if (gSaveBlock1Ptr->location.mapNum != MAP_NUM(LITTLEROOT_TOWN_BRENDANS_HOUSE_1F))
            return PLAYERS_HOUSE_TV_NONE;
    }
    else
    {
        if (gSaveBlock1Ptr->location.mapNum != MAP_NUM(LITTLEROOT_TOWN_MAYS_HOUSE_1F))
            return PLAYERS_HOUSE_TV_NONE;
    }

    if (FlagGet(FLAG_SYS_TV_LATIAS_LATIOS) == TRUE)
        return PLAYERS_HOUSE_TV_LATI;

    if (FlagGet(FLAG_SYS_TV_HOME) == TRUE)
        return PLAYERS_HOUSE_TV_MOVIE;

    return PLAYERS_HOUSE_TV_LATI;
}

void GetMomOrDadStringForTVMessage(void)
{
    if (gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(LITTLEROOT_TOWN_BRENDANS_HOUSE_1F))
    {
        if (gSaveBlock2Ptr->playerGender == MALE)
        {
            if (gSaveBlock1Ptr->location.mapNum == MAP_NUM(LITTLEROOT_TOWN_BRENDANS_HOUSE_1F))
            {
                StringCopy(gStringVar1, gText_Mom);
                VarSet(VAR_TEMP_3, 1);
            }
        }
        else
        {
            if (gSaveBlock1Ptr->location.mapNum == MAP_NUM(LITTLEROOT_TOWN_MAYS_HOUSE_1F))
            {
                StringCopy(gStringVar1, gText_Mom);
                VarSet(VAR_TEMP_3, 1);
            }
        }
    }
    if (VarGet(VAR_TEMP_3) == 1)
    {
        StringCopy(gStringVar1, gText_Mom);
    }
    else if (VarGet(VAR_TEMP_3) == 2)
    {
        StringCopy(gStringVar1, gText_Dad);
    }
    else if (VarGet(VAR_TEMP_3) > 2)
    {
        if (VarGet(VAR_TEMP_3) % 2 == 0)
            StringCopy(gStringVar1, gText_Mom);
        else
            StringCopy(gStringVar1, gText_Dad);
    }
    else
    {
        if (Random() % 2 != 0)
        {
            StringCopy(gStringVar1, gText_Mom);
            VarSet(VAR_TEMP_3, 1);
        }
        else
        {
            StringCopy(gStringVar1, gText_Dad);
            VarSet(VAR_TEMP_3, 2);
        }
    }
}

void HideBattleTowerReporter(void)
{
    VarSet(VAR_BRAVO_TRAINER_BATTLE_TOWER_ON, 0);
    RemoveObjectEventByLocalIdAndMap(LOCALID_BATTLE_TOWER_LOBBY_REPORTER, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup);
    FlagSet(FLAG_HIDE_BATTLE_TOWER_REPORTER);
}

void ReceiveTvShowsData(void *src, u32 size, u8 playersLinkId)
{
    u8 i;
    u16 version;
    TVShow (*rmBuffer2)[MAX_LINK_PLAYERS][TV_SHOWS_COUNT];
    TVShow (*rmBuffer)[MAX_LINK_PLAYERS][TV_SHOWS_COUNT];

    rmBuffer2 = malloc(MAX_LINK_PLAYERS * TV_SHOWS_COUNT * sizeof(TVShow));
    if (rmBuffer2 != NULL)
    {
        for (i = 0; i < MAX_LINK_PLAYERS; i++)
            memcpy((*rmBuffer2)[i], src + i * size, sizeof((*rmBuffer2)[i]));

        rmBuffer = rmBuffer2;
        for (i = 0; i < GetLinkPlayerCount(); i++)
        {
            version = (u8)gLinkPlayers[i].version;
            if (version == VERSION_RUBY || version == VERSION_SAPPHIRE)
                TranslateRubyShows((*rmBuffer)[i]);
            else if (version == VERSION_EMERALD && gLinkPlayers[i].language == LANGUAGE_JAPANESE)
                TranslateJapaneseEmeraldShows((*rmBuffer)[i]);
        }

        // Position player's TV shows in argument list depending on link id
        switch (playersLinkId)
        {
        case 0:
            SetMixedTVShows(gSaveBlock1Ptr->tvShows, (*rmBuffer)[1], (*rmBuffer)[2], (*rmBuffer)[3]);
            break;
        case 1:
            SetMixedTVShows((*rmBuffer)[0], gSaveBlock1Ptr->tvShows, (*rmBuffer)[2], (*rmBuffer)[3]);
            break;
        case 2:
            SetMixedTVShows((*rmBuffer)[0], (*rmBuffer)[1], gSaveBlock1Ptr->tvShows, (*rmBuffer)[3]);
            break;
        case 3:
            SetMixedTVShows((*rmBuffer)[0], (*rmBuffer)[1], (*rmBuffer)[2], gSaveBlock1Ptr->tvShows);
            break;
        }

        CompactTVShowArray(gSaveBlock1Ptr->tvShows);
        DeleteExcessMixedShows();
        CompactTVShowArray(gSaveBlock1Ptr->tvShows);
        DeactivateShowsWithUnseenSpecies();
        DeactivateGameCompleteShowsIfNotUnlocked();
        free(rmBuffer2);
    }
}

static void SetMixedTVShows(TVShow player1[TV_SHOWS_COUNT], TVShow player2[TV_SHOWS_COUNT], TVShow player3[TV_SHOWS_COUNT], TVShow player4[TV_SHOWS_COUNT])
{
    u8 i;
    u8 j;
    TVShow **tvShows[MAX_LINK_PLAYERS];

    tvShows[0] = &player1;
    tvShows[1] = &player2;
    tvShows[2] = &player3;
    tvShows[3] = &player4;
    sTVShowMixingNumPlayers = GetLinkPlayerCount();
    while (1)
    {
        for (i = 0; i < sTVShowMixingNumPlayers; i++)
        {
            if (i == 0)
                sRecordMixingPartnersWithoutShowsToShare = 0;

            sTVShowMixingCurSlot = FindInactiveShowInArray(tvShows[i][0]);
            if (sTVShowMixingCurSlot == -1)
            {
                sRecordMixingPartnersWithoutShowsToShare++;
                if (sRecordMixingPartnersWithoutShowsToShare == sTVShowMixingNumPlayers)
                    return;
            }
            else
            {
                for (j = 0; j < sTVShowMixingNumPlayers - 1; j++)
                {
                    sCurTVShowSlot = FindFirstEmptyRecordMixTVShowSlot(tvShows[(i + j + 1) % sTVShowMixingNumPlayers][0]);
                    if (sCurTVShowSlot != -1
                        && TryMixTVShow(&tvShows[(i + j + 1) % sTVShowMixingNumPlayers][0], &tvShows[i][0], (i + j + 1) % sTVShowMixingNumPlayers) == 1)
                        break;
                }
                if (j == sTVShowMixingNumPlayers - 1)
                    DeleteTVShowInArrayByIdx(tvShows[i][0], sTVShowMixingCurSlot);
            }
        }
    }
}

static bool8 TryMixTVShow(TVShow *dest[TV_SHOWS_COUNT], TVShow *src[TV_SHOWS_COUNT], u8 idx)
{
    bool8 success;
    u8 type;
    TVShow *tv1 = *dest;
    TVShow *tv2 = *src;

    success = FALSE;
    type = GetTVGroupByShowId(tv2[sTVShowMixingCurSlot].common.kind);
    switch (type)
    {
    case TVGROUP_NORMAL:
        success = TryMixNormalTVShow(&tv1[sCurTVShowSlot], &tv2[sTVShowMixingCurSlot], idx);
        break;
    case TVGROUP_RECORD_MIX:
        success = TryMixRecordMixTVShow(&tv1[sCurTVShowSlot], &tv2[sTVShowMixingCurSlot], idx);
        break;
    case TVGROUP_OUTBREAK:
        success = TryMixOutbreakTVShow(&tv1[sCurTVShowSlot], &tv2[sTVShowMixingCurSlot], idx);
        break;
    }

    // Show was mixed, delete from array
    if (success == TRUE)
    {
        DeleteTVShowInArrayByIdx(tv2, sTVShowMixingCurSlot);
        return TRUE;
    }
    return FALSE;
}

static bool8 TryMixNormalTVShow(TVShow *dest, TVShow *src, u8 idx)
{
    u32 linkTrainerId = GetLinkPlayerTrainerId(idx);

    if ((linkTrainerId & 0xFF) == src->common.trainerIdLo
    && ((linkTrainerId >> 8) & 0xFF) == src->common.trainerIdHi)
        return FALSE;

    src->common.trainerIdLo = src->common.srcTrainerIdLo;
    src->common.trainerIdHi = src->common.srcTrainerIdHi;
    src->common.srcTrainerIdLo = linkTrainerId & 0xFF;
    src->common.srcTrainerIdHi = linkTrainerId >> 8;
    *dest = *src;
    dest->common.active = TRUE;
    return TRUE;
}

static bool8 TryMixRecordMixTVShow(TVShow *dest, TVShow *src, u8 idx)
{
    u32 linkTrainerId = GetLinkPlayerTrainerId(idx);

    if ((linkTrainerId & 0xFF) == src->common.srcTrainerIdLo
    && ((linkTrainerId >> 8) & 0xFF) == src->common.srcTrainerIdHi)
        return FALSE;

    if ((linkTrainerId & 0xFF) == src->common.trainerIdLo
    && ((linkTrainerId >> 8) & 0xFF) == src->common.trainerIdHi)
        return FALSE;

    src->common.srcTrainerIdLo = src->common.srcTrainerId2Lo;
    src->common.srcTrainerIdHi = src->common.srcTrainerId2Hi;
    src->common.srcTrainerId2Lo = linkTrainerId & 0xFF;
    src->common.srcTrainerId2Hi = linkTrainerId >> 8;
    *dest = *src;
    dest->common.active = TRUE;
    return TRUE;
}

static bool8 TryMixOutbreakTVShow(TVShow *dest, TVShow *src, u8 idx)
{
    u32 linkTrainerId = GetLinkPlayerTrainerId(idx);

    if ((linkTrainerId & 0xFF) == src->common.trainerIdLo
    && ((linkTrainerId >> 8) & 0xFF) == src->common.trainerIdHi)
        return FALSE;

    src->common.trainerIdLo = src->common.srcTrainerIdLo;
    src->common.trainerIdHi = src->common.srcTrainerIdHi;
    src->common.srcTrainerIdLo = linkTrainerId & 0xFF;
    src->common.srcTrainerIdHi = linkTrainerId >> 8;
    *dest = *src;
    dest->common.active = TRUE;
    dest->massOutbreak.daysLeft = 1;
    return TRUE;
}

static s8 FindInactiveShowInArray(TVShow *tvShows)
{
    u8 i;

    for (i = 0; i < LAST_TVSHOW_IDX; i++)
    {
        // Second check is to make sure its a valid show (not too high, not TVSHOW_OFF_AIR)
        if (tvShows[i].common.active == FALSE && (u8)(tvShows[i].common.kind - 1) < TVGROUP_OUTBREAK_END)
            return i;
    }
    return -1;
}

static void DeactivateShowsWithUnseenSpecies(void)
{
    u16 i;
    u16 species;

    for (i = 0; i < LAST_TVSHOW_IDX; i++)
    {
        switch (gSaveBlock1Ptr->tvShows[i].common.kind)
        {
        case TVSHOW_CONTEST_LIVE_UPDATES:
            species = (&gSaveBlock1Ptr->tvShows[i])->contestLiveUpdates.winningSpecies;
            DeactivateShowIfNotSeenSpecies(species, i);
            species = (&gSaveBlock1Ptr->tvShows[i])->contestLiveUpdates.losingSpecies;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_BATTLE_UPDATE:
            species = (&gSaveBlock1Ptr->tvShows[i])->battleUpdate.speciesPlayer;
            DeactivateShowIfNotSeenSpecies(species, i);
            species = (&gSaveBlock1Ptr->tvShows[i])->battleUpdate.speciesOpponent;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_FAN_CLUB_LETTER:
            species = (&gSaveBlock1Ptr->tvShows[i])->fanclubLetter.species;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_PKMN_FAN_CLUB_OPINIONS:
            species = (&gSaveBlock1Ptr->tvShows[i])->fanclubOpinions.species;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_DUMMY:
            species = (&gSaveBlock1Ptr->tvShows[i])->dummy.species;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_NAME_RATER_SHOW:
            species = (&gSaveBlock1Ptr->tvShows[i])->nameRaterShow.species;
            DeactivateShowIfNotSeenSpecies(species, i);
            species = (&gSaveBlock1Ptr->tvShows[i])->nameRaterShow.randomSpecies;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_BRAVO_TRAINER_POKEMON_PROFILE:
            species = (&gSaveBlock1Ptr->tvShows[i])->bravoTrainer.species;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_BRAVO_TRAINER_BATTLE_TOWER_PROFILE:
            species = (&gSaveBlock1Ptr->tvShows[i])->bravoTrainerTower.species;
            DeactivateShowIfNotSeenSpecies(species, i);
            species = (&gSaveBlock1Ptr->tvShows[i])->bravoTrainerTower.defeatedSpecies;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_POKEMON_TODAY_CAUGHT:
            species = (&gSaveBlock1Ptr->tvShows[i])->pokemonToday.species;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_POKEMON_TODAY_FAILED:
            species = (&gSaveBlock1Ptr->tvShows[i])->pokemonTodayFailed.species;
            DeactivateShowIfNotSeenSpecies(species, i);
            species = (&gSaveBlock1Ptr->tvShows[i])->pokemonTodayFailed.species2;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_FISHING_ADVICE:
            species = (&gSaveBlock1Ptr->tvShows[i])->pokemonAngler.species;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_WORLD_OF_MASTERS:
            species = (&gSaveBlock1Ptr->tvShows[i])->worldOfMasters.species;
            DeactivateShowIfNotSeenSpecies(species, i);
            species = (&gSaveBlock1Ptr->tvShows[i])->worldOfMasters.caughtPoke;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_BREAKING_NEWS:
            species = (&gSaveBlock1Ptr->tvShows[i])->breakingNews.lastOpponentSpecies;
            DeactivateShowIfNotSeenSpecies(species, i);
            species = (&gSaveBlock1Ptr->tvShows[i])->breakingNews.poke1Species;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_SECRET_BASE_VISIT:
            species = (&gSaveBlock1Ptr->tvShows[i])->secretBaseVisit.species;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_BATTLE_SEMINAR:
            species = (&gSaveBlock1Ptr->tvShows[i])->battleSeminar.species;
            DeactivateShowIfNotSeenSpecies(species, i);
            species = (&gSaveBlock1Ptr->tvShows[i])->battleSeminar.foeSpecies;
            DeactivateShowIfNotSeenSpecies(species, i);
            break;
        case TVSHOW_FRONTIER:
            species = (&gSaveBlock1Ptr->tvShows[i])->frontier.species1;
            DeactivateShowIfNotSeenSpecies(species, i);
            species = (&gSaveBlock1Ptr->tvShows[i])->frontier.species2;
            DeactivateShowIfNotSeenSpecies(species, i);
            // Species var re-used here
            species = (&gSaveBlock1Ptr->tvShows[i])->frontier.facilityAndMode;
            switch (species)
            {
            case FRONTIER_SHOW_TOWER_MULTIS:
            case FRONTIER_SHOW_TOWER_LINK_MULTIS:
                break;
            case FRONTIER_SHOW_TOWER_SINGLES:
            case FRONTIER_SHOW_DOME_SINGLES:
            case FRONTIER_SHOW_DOME_DOUBLES:
            case FRONTIER_SHOW_FACTORY_SINGLES:
            case FRONTIER_SHOW_FACTORY_DOUBLES:
            case FRONTIER_SHOW_PIKE:
            case FRONTIER_SHOW_ARENA:
            case FRONTIER_SHOW_PALACE_SINGLES:
            case FRONTIER_SHOW_PALACE_DOUBLES:
            case FRONTIER_SHOW_PYRAMID:
                species = (&gSaveBlock1Ptr->tvShows[i])->frontier.species3;
                DeactivateShowIfNotSeenSpecies(species, i);
                break;
            case FRONTIER_SHOW_TOWER_DOUBLES:
                species = (&gSaveBlock1Ptr->tvShows[i])->frontier.species3;
                DeactivateShowIfNotSeenSpecies(species, i);
                species = (&gSaveBlock1Ptr->tvShows[i])->frontier.species4;
                DeactivateShowIfNotSeenSpecies(species, i);
                break;
            }
            break;
        // Shows with no species
        case TVSHOW_OFF_AIR:
        case TVSHOW_RECENT_HAPPENINGS:
        case TVSHOW_3_CHEERS_FOR_POKEBLOCKS:
        case TVSHOW_TODAYS_RIVAL_TRAINER:
        case TVSHOW_TREND_WATCHER:
        case TVSHOW_TREASURE_INVESTIGATORS:
        case TVSHOW_FIND_THAT_GAMER:
        case TVSHOW_TRAINER_FAN_CLUB:
        case TVSHOW_CUTIES:
        case TVSHOW_SMART_SHOPPER:
        case TVSHOW_FAN_CLUB_SPECIAL:
        case TVSHOW_LILYCOVE_CONTEST_LADY:
        case TVSHOW_LOTTO_WINNER:
        case TVSHOW_NUMBER_ONE:
        case TVSHOW_SECRET_BASE_SECRETS:
        case TVSHOW_SAFARI_FAN_CLUB:
        case TVSHOW_MASS_OUTBREAK:
            break;
        default:
            DeactivateShow(i);
            break;
        }
    }
}

static void DeactivateShow(u8 showIdx)
{
    gSaveBlock1Ptr->tvShows[showIdx].common.active = FALSE;
}

static void DeactivateShowIfNotSeenSpecies(u16 species, u8 showIdx)
{
    if (!GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_SEEN))
        gSaveBlock1Ptr->tvShows[showIdx].common.active = FALSE;
}

static void DeactivateGameCompleteShowsIfNotUnlocked(void)
{
    u16 i;

    if (FlagGet(FLAG_SYS_GAME_CLEAR) != TRUE)
    {
        for (i = 0; i < LAST_TVSHOW_IDX; i++)
        {
            if (gSaveBlock1Ptr->tvShows[i].common.kind == TVSHOW_BRAVO_TRAINER_BATTLE_TOWER_PROFILE)
                gSaveBlock1Ptr->tvShows[i].common.active = FALSE;
            else if (gSaveBlock1Ptr->tvShows[i].common.kind == TVSHOW_MASS_OUTBREAK)
                gSaveBlock1Ptr->tvShows[i].common.active = FALSE;
        }
    }
}

void DeactivateAllNormalTVShows(void)
{
    u8 i;

    for (i = 0; i < NUM_NORMAL_TVSHOW_SLOTS; i++)
    {
        if (GetTVGroupByShowId(gSaveBlock1Ptr->tvShows[i].common.kind) == TVGROUP_NORMAL)
            gSaveBlock1Ptr->tvShows[i].common.active = FALSE;
    }
}

// Ensures a minimum of 5 empty record mixed show slots
static void DeleteExcessMixedShows(void)
{
    s8 i;
    s8 numEmptyMixSlots = 0;
    for (i = NUM_NORMAL_TVSHOW_SLOTS; i < LAST_TVSHOW_IDX; i++)
    {
        if (gSaveBlock1Ptr->tvShows[i].common.kind == TVSHOW_OFF_AIR)
            numEmptyMixSlots++;
    }
    for (i = 0; i < NUM_NORMAL_TVSHOW_SLOTS - numEmptyMixSlots; i++)
        DeleteTVShowInArrayByIdx(gSaveBlock1Ptr->tvShows, i + NUM_NORMAL_TVSHOW_SLOTS);
}

void ReceivePokeNewsData(void *src, u32 size, u8 playersLinkId)
{
    u8 i;
    PokeNews (*rmBuffer2)[MAX_LINK_PLAYERS][POKE_NEWS_COUNT];
    PokeNews (*rmBuffer)[MAX_LINK_PLAYERS][POKE_NEWS_COUNT];

    rmBuffer2 = malloc(MAX_LINK_PLAYERS * POKE_NEWS_COUNT * sizeof(PokeNews));
    if (rmBuffer2 != NULL)
    {
        for (i = 0; i < MAX_LINK_PLAYERS; i++)
            memcpy((*rmBuffer2)[i], src + i * size, sizeof((*rmBuffer2)[i]));

        rmBuffer = rmBuffer2;

        // Position player's PokeNews in argument list depending on link id
        switch (playersLinkId)
        {
        case 0:
            SetMixedPokeNews(gSaveBlock1Ptr->pokeNews, (*rmBuffer)[1], (*rmBuffer)[2], (*rmBuffer)[3]);
            break;
        case 1:
            SetMixedPokeNews((*rmBuffer)[0], gSaveBlock1Ptr->pokeNews, (*rmBuffer)[2], (*rmBuffer)[3]);
            break;
        case 2:
            SetMixedPokeNews((*rmBuffer)[0], (*rmBuffer)[1], gSaveBlock1Ptr->pokeNews, (*rmBuffer)[3]);
            break;
        case 3:
            SetMixedPokeNews((*rmBuffer)[0], (*rmBuffer)[1], (*rmBuffer)[2], gSaveBlock1Ptr->pokeNews);
            break;
        }
        ClearInvalidPokeNews();
        ClearPokeNewsIfGameNotComplete();
        free(rmBuffer2);
    }
}

static void SetMixedPokeNews(PokeNews player1[POKE_NEWS_COUNT], PokeNews player2[POKE_NEWS_COUNT], PokeNews player3[POKE_NEWS_COUNT], PokeNews player4[POKE_NEWS_COUNT])
{
    u8 i, j, k;
    PokeNews **pokeNews[MAX_LINK_PLAYERS];

    pokeNews[0] = &player1;
    pokeNews[1] = &player2;
    pokeNews[2] = &player3;
    pokeNews[3] = &player4;
    sTVShowNewsMixingNumPlayers = GetLinkPlayerCount();
    for (i = 0; i < POKE_NEWS_COUNT; i++)
    {
        for (j = 0; j < sTVShowNewsMixingNumPlayers; j++)
        {
            sTVShowMixingCurSlot = GetPokeNewsSlotIfActive(*pokeNews[j], i);
            if (sTVShowMixingCurSlot != -1)
            {
                for (k = 0; k < sTVShowNewsMixingNumPlayers - 1; k++)
                {
                    sCurTVShowSlot = GetFirstEmptyPokeNewsSlot(*pokeNews[(j + k + 1) % sTVShowNewsMixingNumPlayers]);
                    if (sCurTVShowSlot != -1)
                        InitTryMixPokeNewsShow(pokeNews[(j + k + 1) % sTVShowNewsMixingNumPlayers], pokeNews[j]);
                }
            }
        }
    }
}

static void InitTryMixPokeNewsShow(PokeNews *dest[POKE_NEWS_COUNT], PokeNews *src[POKE_NEWS_COUNT])
{
    PokeNews *ptr1 = *dest;
    PokeNews *ptr2 = *src;
    ptr2 += sTVShowMixingCurSlot;
    TryMixPokeNewsShow(ptr1, ptr2, sCurTVShowSlot);
}

static bool8 TryMixPokeNewsShow(PokeNews *dest, PokeNews *src, s8 slot)
{
    u8 i;

    if (src->kind == POKENEWS_NONE)
        return FALSE;

    for (i = 0; i < POKE_NEWS_COUNT; i++)
    {
        if (dest[i].kind == src->kind)
            return FALSE;
    }
    dest[slot].kind = src->kind;
    dest[slot].state = POKENEWS_STATE_UPCOMING;
    dest[slot].dayCountdown = src->dayCountdown;
    return TRUE;
}

static s8 GetPokeNewsSlotIfActive(PokeNews *pokeNews, u8 idx)
{
    if (pokeNews[idx].kind == POKENEWS_NONE)
        return -1;

    return idx;
}

static void ClearInvalidPokeNews(void)
{
    u8 i;

    for (i = 0; i < POKE_NEWS_COUNT; i++)
    {
        if (gSaveBlock1Ptr->pokeNews[i].kind > POKENEWS_BLENDMASTER)
            ClearPokeNewsBySlot(i);
    }
    CompactPokeNews();
}

static void ClearPokeNewsIfGameNotComplete(void)
{
    u8 i;

    if (FlagGet(FLAG_SYS_GAME_CLEAR) != TRUE)
    {
        for (i = 0; i < POKE_NEWS_COUNT; i++)
            gSaveBlock1Ptr->pokeNews[i].state = POKENEWS_STATE_INACTIVE;
    }
}

#define SetStrLanguage(strptr, langptr, langfix) \
if (IsStringJapanese(strptr)) \
{   \
    (langptr) = LANGUAGE_JAPANESE; \
} \
else \
{ \
    (langptr) = langfix; \
}

// Unused
static void TranslateShowNames(TVShow *show, u32 language)
{
    int i;
    TVShow **shows;

    shows = calloc(11, sizeof(TVShow *));
    for (i = 0; i < LAST_TVSHOW_IDX; i++)
    {
        switch (show[i].common.kind)
        {
        case TVSHOW_FAN_CLUB_LETTER:
        case TVSHOW_RECENT_HAPPENINGS: // NOTE: These two shows are assumed to have the same struct layout
            shows[0] = &show[i];
            SetStrLanguage(shows[0]->fanclubLetter.playerName, shows[0]->fanclubLetter.language, language);
            break;
        case TVSHOW_PKMN_FAN_CLUB_OPINIONS:
            shows[1] = &show[i];
            SetStrLanguage(shows[1]->fanclubOpinions.playerName, shows[1]->fanclubOpinions.language, language);
            SetStrLanguage(shows[1]->fanclubOpinions.nickname, shows[1]->fanclubOpinions.pokemonNameLanguage, language);
            break;
        case TVSHOW_POKEMON_TODAY_CAUGHT:
            shows[6] = &show[i];
            SetStrLanguage(shows[6]->pokemonToday.playerName, shows[6]->pokemonToday.language, language);
            SetStrLanguage(shows[6]->pokemonToday.nickname, shows[6]->pokemonToday.language2, language);
            break;
        case TVSHOW_SMART_SHOPPER:
            shows[7] = &show[i];
            SetStrLanguage(shows[7]->smartshopperShow.playerName, shows[7]->smartshopperShow.language, language);
            break;
        case TVSHOW_BRAVO_TRAINER_BATTLE_TOWER_PROFILE:
            shows[5] = &show[i];
            SetStrLanguage(shows[5]->bravoTrainerTower.trainerName, shows[5]->bravoTrainerTower.language, language);
            SetStrLanguage(shows[5]->bravoTrainerTower.pokemonName, shows[5]->bravoTrainerTower.pokemonNameLanguage, language);
            break;
        case TVSHOW_BRAVO_TRAINER_POKEMON_PROFILE:
            shows[4] = &show[i];
            SetStrLanguage(shows[4]->bravoTrainer.playerName, shows[4]->bravoTrainer.language, language);
            SetStrLanguage(shows[4]->bravoTrainer.pokemonNickname, shows[4]->bravoTrainer.pokemonNameLanguage, language);
            break;
        case TVSHOW_NAME_RATER_SHOW:
            shows[3] = &show[i];
            SetStrLanguage(shows[3]->nameRaterShow.trainerName, shows[3]->nameRaterShow.language, language);
            SetStrLanguage(shows[3]->nameRaterShow.pokemonName, shows[3]->nameRaterShow.pokemonNameLanguage, language);
            break;
        case TVSHOW_POKEMON_TODAY_FAILED:
            shows[2] = &show[i];
            SetStrLanguage(shows[2]->pokemonTodayFailed.playerName, shows[2]->pokemonTodayFailed.language, language);
            break;
        case TVSHOW_FISHING_ADVICE:
            shows[8] = &show[i];
            SetStrLanguage(shows[8]->pokemonAngler.playerName, shows[8]->pokemonAngler.language, language);
            break;
        case TVSHOW_WORLD_OF_MASTERS:
            shows[9] = &show[i];
            SetStrLanguage(shows[9]->worldOfMasters.playerName, shows[9]->worldOfMasters.language, language);
            break;
        case TVSHOW_MASS_OUTBREAK:
            shows[10] = &show[i];
            shows[10]->massOutbreak.language = language;
            break;
        }
    }
    free(shows);
}

void SanitizeTVShowsForRuby(TVShow *shows)
{
    TVShow *curShow;

    SanitizeTVShowLocationsForRuby(shows);
    for (curShow = shows; curShow < shows + LAST_TVSHOW_IDX; curShow++)
    {
        if (curShow->bravoTrainerTower.kind == TVSHOW_BRAVO_TRAINER_BATTLE_TOWER_PROFILE)
        {
            if ((curShow->bravoTrainerTower.language == LANGUAGE_JAPANESE && curShow->bravoTrainerTower.pokemonNameLanguage != LANGUAGE_JAPANESE)
             || (curShow->bravoTrainerTower.language != LANGUAGE_JAPANESE && curShow->bravoTrainerTower.pokemonNameLanguage == LANGUAGE_JAPANESE))
                memset(curShow, 0, sizeof(TVShow));
        }
    }
}

static void TranslateRubyShows(TVShow *shows)
{
    TVShow *curShow;

    for (curShow = shows; curShow < shows + LAST_TVSHOW_IDX; curShow++)
    {
        if (curShow->bravoTrainerTower.kind == TVSHOW_BRAVO_TRAINER_BATTLE_TOWER_PROFILE)
        {
            if (IsStringJapanese(curShow->bravoTrainerTower.pokemonName))
                curShow->bravoTrainerTower.pokemonNameLanguage = LANGUAGE_JAPANESE;
            else
                curShow->bravoTrainerTower.pokemonNameLanguage = GAME_LANGUAGE;
        }
    }
}

static u8 GetStringLanguage(u8 *str)
{
    return IsStringJapanese(str) ? LANGUAGE_JAPANESE : GAME_LANGUAGE;
}

static void TranslateJapaneseEmeraldShows(TVShow *shows)
{
    TVShow *curShow;

    for (curShow = shows; curShow < shows + LAST_TVSHOW_IDX; curShow++)
    {
        switch(curShow->common.kind)
        {
        case TVSHOW_FAN_CLUB_LETTER:
            curShow->fanclubLetter.language = GetStringLanguage(curShow->fanclubLetter.playerName);
            break;
        case TVSHOW_RECENT_HAPPENINGS:
            curShow->recentHappenings.language = GetStringLanguage(curShow->recentHappenings.playerName);
            break;
        case TVSHOW_PKMN_FAN_CLUB_OPINIONS:
            curShow->fanclubOpinions.language = GetStringLanguage(curShow->fanclubOpinions.playerName);
            curShow->fanclubOpinions.pokemonNameLanguage = GetStringLanguage(curShow->fanclubOpinions.nickname);
            break;
        case TVSHOW_DUMMY:
            curShow->dummy.language = GetStringLanguage(curShow->dummy.name);
            break;
        case TVSHOW_NAME_RATER_SHOW:
            curShow->nameRaterShow.language = GetStringLanguage(curShow->nameRaterShow.trainerName);
            curShow->nameRaterShow.pokemonNameLanguage = GetStringLanguage(curShow->nameRaterShow.pokemonName);
            break;
        case TVSHOW_BRAVO_TRAINER_POKEMON_PROFILE:
            curShow->bravoTrainer.language = GetStringLanguage(curShow->bravoTrainer.playerName);
            curShow->bravoTrainer.pokemonNameLanguage = GetStringLanguage(curShow->bravoTrainer.pokemonNickname);
            break;
        case TVSHOW_BRAVO_TRAINER_BATTLE_TOWER_PROFILE:
            curShow->bravoTrainerTower.language = GetStringLanguage(curShow->bravoTrainerTower.trainerName);
            curShow->bravoTrainerTower.pokemonNameLanguage = GetStringLanguage(curShow->bravoTrainerTower.pokemonName);
            break;
        case TVSHOW_CONTEST_LIVE_UPDATES:
            curShow->contestLiveUpdates.winningTrainerLanguage = GetStringLanguage(curShow->contestLiveUpdates.winningTrainerName);
            curShow->contestLiveUpdates.losingTrainerLanguage = GetStringLanguage(curShow->contestLiveUpdates.losingTrainerName);
            break;
        case TVSHOW_3_CHEERS_FOR_POKEBLOCKS:
            curShow->threeCheers.language = GetStringLanguage(curShow->threeCheers.playerName);
            curShow->threeCheers.worstBlenderLanguage = GetStringLanguage(curShow->threeCheers.worstBlenderName);
            break;
        case TVSHOW_BATTLE_UPDATE:
            curShow->battleUpdate.language = GetStringLanguage(curShow->battleUpdate.playerName);
            curShow->battleUpdate.linkOpponentLanguage = GetStringLanguage(curShow->battleUpdate.linkOpponentName);
            break;
        case TVSHOW_FAN_CLUB_SPECIAL:
            curShow->fanClubSpecial.language = GetStringLanguage(curShow->fanClubSpecial.playerName);
            curShow->fanClubSpecial.idolNameLanguage = GetStringLanguage(curShow->fanClubSpecial.idolName);
            break;
        case TVSHOW_LILYCOVE_CONTEST_LADY:
            curShow->contestLady.language = GetStringLanguage(curShow->contestLady.playerName);
            curShow->contestLady.pokemonNameLanguage = GetStringLanguage(curShow->contestLady.nickname);
            break;
        case TVSHOW_POKEMON_TODAY_CAUGHT:
            curShow->pokemonToday.language = GetStringLanguage(curShow->pokemonToday.playerName);
            curShow->pokemonToday.language2 = GetStringLanguage(curShow->pokemonToday.nickname);
            break;
        case TVSHOW_SMART_SHOPPER:
            curShow->smartshopperShow.language = GetStringLanguage(curShow->smartshopperShow.playerName);
            break;
        case TVSHOW_POKEMON_TODAY_FAILED:
            curShow->pokemonTodayFailed.language = GetStringLanguage(curShow->pokemonTodayFailed.playerName);
            break;
        case TVSHOW_FISHING_ADVICE:
            curShow->pokemonAngler.language = GetStringLanguage(curShow->pokemonAngler.playerName);
            break;
        case TVSHOW_WORLD_OF_MASTERS:
            curShow->worldOfMasters.language = GetStringLanguage(curShow->worldOfMasters.playerName);
            break;
        case TVSHOW_TREND_WATCHER:
            curShow->trendWatcher.language = GetStringLanguage(curShow->trendWatcher.playerName);
            break;
        case TVSHOW_BREAKING_NEWS:
            curShow->breakingNews.language = GetStringLanguage(curShow->breakingNews.playerName);
            break;
        case TVSHOW_BATTLE_SEMINAR:
            curShow->battleSeminar.language = GetStringLanguage(curShow->battleSeminar.playerName);
            break;
        case TVSHOW_FIND_THAT_GAMER:
        case TVSHOW_TRAINER_FAN_CLUB:
            curShow->trainerFanClub.language = GetStringLanguage(curShow->trainerFanClub.playerName);
            break;
        case TVSHOW_CUTIES:
            curShow->cuties.language = GetStringLanguage(curShow->cuties.playerName);
            curShow->cuties.pokemonNameLanguage = GetStringLanguage(curShow->cuties.nickname);
            break;
        case TVSHOW_TODAYS_RIVAL_TRAINER:
        case TVSHOW_SECRET_BASE_VISIT:
        case TVSHOW_FRONTIER:
            curShow->rivalTrainer.language = GetStringLanguage(curShow->rivalTrainer.playerName);
            break;
        case TVSHOW_TREASURE_INVESTIGATORS:
        case TVSHOW_LOTTO_WINNER:
        case TVSHOW_NUMBER_ONE:
            curShow->treasureInvestigators.language = GetStringLanguage(curShow->treasureInvestigators.playerName);
            break;
        case TVSHOW_SECRET_BASE_SECRETS:
            curShow->secretBaseSecrets.language = GetStringLanguage(curShow->secretBaseSecrets.playerName);
            curShow->secretBaseSecrets.baseOwnersNameLanguage = GetStringLanguage(curShow->secretBaseSecrets.baseOwnersName);
            break;
        case TVSHOW_SAFARI_FAN_CLUB:
            curShow->safariFanClub.language = GetStringLanguage(curShow->safariFanClub.playerName);
            break;
        case TVSHOW_MASS_OUTBREAK:
            break;
        }
    }
}

void SanitizeTVShowLocationsForRuby(TVShow *shows)
{
    int i;

    for (i = 0; i < LAST_TVSHOW_IDX; i++)
    {
        switch (shows[i].common.kind)
        {
        case TVSHOW_WORLD_OF_MASTERS:
            if (shows[i].worldOfMasters.location > KANTO_MAPSEC_START)
                memset(&shows[i], 0, sizeof(TVShow));
            break;
        case TVSHOW_POKEMON_TODAY_FAILED:
            if (shows[i].pokemonTodayFailed.location > KANTO_MAPSEC_START)
                memset(&shows[i], 0, sizeof(TVShow));
            break;
        }
    }
}

// gSpecialVar_0x8004 here is set from GetRandomActiveShowIdx in EventScript_TryDoTVShow
void DoTVShow(void)
{
    if (gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004].common.active)
    {
        switch (gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004].common.kind)
        {
        case TVSHOW_FAN_CLUB_LETTER:
            DoTVShowPokemonFanClubLetter();
            break;
        case TVSHOW_RECENT_HAPPENINGS:
            DoTVShowRecentHappenings();
            break;
        case TVSHOW_PKMN_FAN_CLUB_OPINIONS:
            DoTVShowPokemonFanClubOpinions();
            break;
        case TVSHOW_DUMMY:
            DoTVShowDummiedOut();
            break;
        case TVSHOW_MASS_OUTBREAK:
            DoTVShowPokemonNewsMassOutbreak();
            break;
        case TVSHOW_BRAVO_TRAINER_POKEMON_PROFILE:
            DoTVShowBravoTrainerPokemonProfile();
            break;
        case TVSHOW_BRAVO_TRAINER_BATTLE_TOWER_PROFILE:
            DoTVShowBravoTrainerBattleTower();
            break;
        case TVSHOW_POKEMON_TODAY_CAUGHT:
            DoTVShowPokemonTodaySuccessfulCapture();
            break;
        case TVSHOW_SMART_SHOPPER:
            DoTVShowTodaysSmartShopper();
            break;
        case TVSHOW_NAME_RATER_SHOW:
            DoTVShowTheNameRaterShow();
            break;
        case TVSHOW_CONTEST_LIVE_UPDATES:
            DoTVShowPokemonContestLiveUpdates();
            break;
        case TVSHOW_BATTLE_UPDATE:
            DoTVShowPokemonBattleUpdate();
            break;
        case TVSHOW_3_CHEERS_FOR_POKEBLOCKS:
            DoTVShow3CheersForPokeblocks();
            break;
        case TVSHOW_POKEMON_TODAY_FAILED:
            DoTVShowPokemonTodayFailedCapture();
            break;
        case TVSHOW_FISHING_ADVICE:
            DoTVShowPokemonAngler();
            break;
        case TVSHOW_WORLD_OF_MASTERS:
            DoTVShowTheWorldOfMasters();
            break;
        case TVSHOW_TODAYS_RIVAL_TRAINER:
            DoTVShowTodaysRivalTrainer();
            break;
        case TVSHOW_TREND_WATCHER:
            DoTVShowDewfordTrendWatcherNetwork();
            break;
        case TVSHOW_TREASURE_INVESTIGATORS:
            DoTVShowHoennTreasureInvestigators();
            break;
        case TVSHOW_FIND_THAT_GAMER:
            DoTVShowFindThatGamer();
            break;
        case TVSHOW_BREAKING_NEWS:
            DoTVShowBreakingNewsTV();
            break;
        case TVSHOW_SECRET_BASE_VISIT:
            DoTVShowSecretBaseVisit();
            break;
        case TVSHOW_LOTTO_WINNER:
            DoTVShowPokemonLotteryWinnerFlashReport();
            break;
        case TVSHOW_BATTLE_SEMINAR:
            DoTVShowThePokemonBattleSeminar();
            break;
        case TVSHOW_FAN_CLUB_SPECIAL:
            DoTVShowTrainerFanClubSpecial();
            break;
        case TVSHOW_TRAINER_FAN_CLUB:
            DoTVShowTrainerFanClub();
            break;
        case TVSHOW_CUTIES:
            DoTVShowSpotTheCuties();
            break;
        case TVSHOW_FRONTIER:
            DoTVShowPokemonNewsBattleFrontier();
            break;
        case TVSHOW_NUMBER_ONE:
            DoTVShowWhatsNo1InHoennToday();
            break;
        case TVSHOW_SECRET_BASE_SECRETS:
            DoTVShowSecretBaseSecrets();
            break;
        case TVSHOW_SAFARI_FAN_CLUB:
            DoTVShowSafariFanClub();
            break;
        case TVSHOW_LILYCOVE_CONTEST_LADY:
            DoTVShowLilycoveContestLady();
            break;
        }
    }
}

static void DoTVShowBravoTrainerPokemonProfile(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainer.playerName, show->bravoTrainer.language);
        CopyContestCategoryToStringVar(1, show->bravoTrainer.contestCategory);
        CopyContestRankToStringVar(2, show->bravoTrainer.contestRank);
        if (!StringCompare(gSpeciesNames[show->bravoTrainer.species], show->bravoTrainer.pokemonNickname))
            sTVShowState = 8;
        else
            sTVShowState = 1;
        break;
    case 1:
        StringCopy(gStringVar1, gSpeciesNames[show->bravoTrainer.species]);
        TVShowConvertInternationalString(gStringVar2, show->bravoTrainer.pokemonNickname, show->bravoTrainer.pokemonNameLanguage);
        CopyContestCategoryToStringVar(2, show->bravoTrainer.contestCategory);
        sTVShowState = 2;
        break;
    case 2:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainer.playerName, show->bravoTrainer.language);
        if (show->bravoTrainer.contestResult == 0) // placed first
            sTVShowState = 3;
        else
            sTVShowState = 4;
        break;
    case 3:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainer.playerName, show->bravoTrainer.language);
        CopyEasyChatWord(gStringVar2, show->bravoTrainer.words[0]);
        ConvertIntToDecimalString(2, show->bravoTrainer.contestResult + 1);
        sTVShowState = 5;
        break;
    case 4:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainer.playerName, show->bravoTrainer.language);
        CopyEasyChatWord(gStringVar2, show->bravoTrainer.words[0]);
        ConvertIntToDecimalString(2, show->bravoTrainer.contestResult + 1);
        sTVShowState = 5;
        break;
    case 5:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainer.playerName, show->bravoTrainer.language);
        CopyContestCategoryToStringVar(1, show->bravoTrainer.contestCategory);
        CopyEasyChatWord(gStringVar3, show->bravoTrainer.words[1]);
        if (show->bravoTrainer.move)
            sTVShowState = 6;
        else
            sTVShowState = 7;
        break;
    case 6:
        StringCopy(gStringVar1, gSpeciesNames[show->bravoTrainer.species]);
        StringCopy(gStringVar2, gMoveNames[show->bravoTrainer.move]);
        CopyEasyChatWord(gStringVar3, show->bravoTrainer.words[1]);
        sTVShowState = 7;
        break;
    case 7:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainer.playerName, show->bravoTrainer.language);
        StringCopy(gStringVar2, gSpeciesNames[show->bravoTrainer.species]);
        TVShowDone();
        break;
    case 8:
        StringCopy(gStringVar1, gSpeciesNames[show->bravoTrainer.species]);
        sTVShowState = 2;
        break;
    }
    ShowFieldMessage(sTVBravoTrainerTextGroup[state]);
}

static void DoTVShowBravoTrainerBattleTower(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch(state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainerTower.trainerName, show->bravoTrainerTower.language);
        StringCopy(gStringVar2, gSpeciesNames[show->bravoTrainerTower.species]);
        if (show->bravoTrainerTower.numFights >= 7)
            sTVShowState = 1;
        else
            sTVShowState = 2;
        break;
    case 1:
        if (show->bravoTrainerTower.btLevel == 50)
        {
            StringCopy(gStringVar1, gText_Lv50);
        }
        else
        {
            StringCopy(gStringVar1, gText_OpenLevel);
        }
        ConvertIntToDecimalString(1, show->bravoTrainerTower.numFights);
        if (show->bravoTrainerTower.wonTheChallenge == TRUE)
            sTVShowState = 3;
        else
            sTVShowState = 4;
        break;
    case 2:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainerTower.pokemonName, show->bravoTrainerTower.pokemonNameLanguage);
        ConvertIntToDecimalString(1, show->bravoTrainerTower.numFights + 1);
        if (show->bravoTrainerTower.interviewResponse == 0)
            sTVShowState = 5;
        else
            sTVShowState = 6;
        break;
    case 3:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainerTower.pokemonName, show->bravoTrainerTower.pokemonNameLanguage);
        StringCopy(gStringVar2, gSpeciesNames[show->bravoTrainerTower.defeatedSpecies]);
        if (show->bravoTrainerTower.interviewResponse == 0)
            sTVShowState = 5;
        else
            sTVShowState = 6;
        break;
    case 4:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainerTower.pokemonName, show->bravoTrainerTower.pokemonNameLanguage);
        StringCopy(gStringVar2, gSpeciesNames[show->bravoTrainerTower.defeatedSpecies]);
        if (show->bravoTrainerTower.interviewResponse == 0)
            sTVShowState = 5;
        else
            sTVShowState = 6;
        break;
    case 5:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainerTower.pokemonName, show->bravoTrainerTower.pokemonNameLanguage);
        sTVShowState = 11;
        break;
    case 6:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainerTower.pokemonName, show->bravoTrainerTower.pokemonNameLanguage);
        sTVShowState = 11;
        break;
    case 7:
        sTVShowState = 11;
        break;
    case 8:
    case 9:
    case 10:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainerTower.trainerName, show->bravoTrainerTower.language);
        sTVShowState = 11;
        break;
    case 11:
        CopyEasyChatWord(gStringVar1, show->bravoTrainerTower.words[0]);
        if (show->bravoTrainerTower.interviewResponse == 0)
            sTVShowState = 12;
        else
            sTVShowState = 13;
        break;
    case 12:
    case 13:
        CopyEasyChatWord(gStringVar1, show->bravoTrainerTower.words[0]);
        TVShowConvertInternationalString(gStringVar2, show->bravoTrainerTower.trainerName, show->bravoTrainerTower.language);
        TVShowConvertInternationalString(gStringVar3, show->bravoTrainerTower.pokemonName, show->bravoTrainerTower.pokemonNameLanguage);
        sTVShowState = 14;
        break;
    case 14:
        TVShowConvertInternationalString(gStringVar1, show->bravoTrainerTower.trainerName, show->bravoTrainerTower.language);
        StringCopy(gStringVar2, gSpeciesNames[show->bravoTrainerTower.species]);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVBravoTrainerBattleTowerTextGroup[state]);
}

static void DoTVShowTodaysSmartShopper(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch(state)
    {
    case SMARTSHOPPER_STATE_INTRO:
        TVShowConvertInternationalString(gStringVar1, show->smartshopperShow.playerName, show->smartshopperShow.language);
        GetMapName(gStringVar2, show->smartshopperShow.shopLocation, 0);
        if (show->smartshopperShow.itemAmounts[0] >= 255)
            sTVShowState = SMARTSHOPPER_STATE_CLERK_MAX;
        else
            sTVShowState = SMARTSHOPPER_STATE_CLERK_NORMAL;
        break;
    case SMARTSHOPPER_STATE_CLERK_NORMAL:
        TVShowConvertInternationalString(gStringVar1, show->smartshopperShow.playerName, show->smartshopperShow.language);
        StringCopy(gStringVar2, ItemId_GetName(show->smartshopperShow.itemIds[0]));
        ConvertIntToDecimalString(2, show->smartshopperShow.itemAmounts[0]);
        // Pick a random comment (SMARTSHOPPER_STATE_RAND_COMMENT_#)
        sTVShowState += SMARTSHOPPER_STATE_CLERK_NORMAL + (Random() % (SMARTSHOPPER_STATE_RAND_COMMENT_4 - SMARTSHOPPER_STATE_RAND_COMMENT_1 + 1));
        break;
    case SMARTSHOPPER_STATE_RAND_COMMENT_1:
    case SMARTSHOPPER_STATE_RAND_COMMENT_3:
    case SMARTSHOPPER_STATE_RAND_COMMENT_4:
        if (show->smartshopperShow.itemIds[1] != ITEM_NONE)
            sTVShowState = SMARTSHOPPER_STATE_SECOND_ITEM;
        else
            sTVShowState = SMARTSHOPPER_STATE_IS_VIP;
        break;
    case SMARTSHOPPER_STATE_RAND_COMMENT_2:
        ConvertIntToDecimalString(2, show->smartshopperShow.itemAmounts[0] + 1);
        if (show->smartshopperShow.itemIds[1] != ITEM_NONE)
            sTVShowState = SMARTSHOPPER_STATE_SECOND_ITEM;
        else
            sTVShowState = SMARTSHOPPER_STATE_IS_VIP;
        break;
    case SMARTSHOPPER_STATE_SECOND_ITEM:
        // Clerk describes 2nd type of item player purchased
        StringCopy(gStringVar2, ItemId_GetName(show->smartshopperShow.itemIds[1]));
        ConvertIntToDecimalString(2, show->smartshopperShow.itemAmounts[1]);
        if (show->smartshopperShow.itemIds[2] != ITEM_NONE)
            sTVShowState = SMARTSHOPPER_STATE_THIRD_ITEM;
        else if (show->smartshopperShow.priceReduced == TRUE)
            sTVShowState = SMARTSHOPPER_STATE_DURING_SALE;
        else
            sTVShowState = SMARTSHOPPER_STATE_OUTRO_NORMAL;
        break;
    case SMARTSHOPPER_STATE_THIRD_ITEM:
        // Clerk describes 3rd type of item player purchased
        StringCopy(gStringVar2, ItemId_GetName(show->smartshopperShow.itemIds[2]));
        ConvertIntToDecimalString(2, show->smartshopperShow.itemAmounts[2]);
        if (show->smartshopperShow.priceReduced == TRUE)
            sTVShowState = SMARTSHOPPER_STATE_DURING_SALE;
        else
            sTVShowState = SMARTSHOPPER_STATE_OUTRO_NORMAL;
        break;
    case SMARTSHOPPER_STATE_DURING_SALE:
        if (show->smartshopperShow.itemAmounts[0] >= 255)
            sTVShowState = SMARTSHOPPER_STATE_OUTRO_MAX;
        else
            sTVShowState = SMARTSHOPPER_STATE_OUTRO_NORMAL;
        break;
    case SMARTSHOPPER_STATE_OUTRO_NORMAL:
        SmartShopper_BufferPurchaseTotal(1, show);
        TVShowDone();
        break;
    case SMARTSHOPPER_STATE_IS_VIP:
        // Clerk says customer is a VIP
        // Said if player only purchased one type of item
        if (show->smartshopperShow.priceReduced == TRUE)
            sTVShowState = SMARTSHOPPER_STATE_DURING_SALE;
        else
            sTVShowState = SMARTSHOPPER_STATE_OUTRO_NORMAL;
        break;
    case SMARTSHOPPER_STATE_CLERK_MAX:
        // Clerk's comments if player purchased maximum number of 1st item
        TVShowConvertInternationalString(gStringVar1, show->smartshopperShow.playerName, show->smartshopperShow.language);
        StringCopy(gStringVar2, ItemId_GetName(show->smartshopperShow.itemIds[0]));
        if (show->smartshopperShow.priceReduced == TRUE)
            sTVShowState = SMARTSHOPPER_STATE_DURING_SALE;
        else
            sTVShowState = SMARTSHOPPER_STATE_OUTRO_MAX;
        break;
    case SMARTSHOPPER_STATE_OUTRO_MAX:
        // Outro comments if player purchased maximum number of 1st item
        TVShowConvertInternationalString(gStringVar1, show->smartshopperShow.playerName, show->smartshopperShow.language);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVTodaysSmartShopperTextGroup[state]);
}

static void DoTVShowTheNameRaterShow(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->nameRaterShow.trainerName, show->nameRaterShow.language);
        StringCopy(gStringVar2, gSpeciesNames[show->nameRaterShow.species]);
        TVShowConvertInternationalString(gStringVar3, show->nameRaterShow.pokemonName, show->nameRaterShow.pokemonNameLanguage);
        sTVShowState = GetRandomNameRaterStateFromName(show) + 1;
        break;
    case 1:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
        if (show->nameRaterShow.random == 0)
            sTVShowState = 9;
        else if (show->nameRaterShow.random == 1)
            sTVShowState = 10;
        else if (show->nameRaterShow.random == 2)
            sTVShowState = 11;
        break;
    case 2:
        TVShowConvertInternationalString(gStringVar1, show->nameRaterShow.trainerName, show->nameRaterShow.language);
        if (show->nameRaterShow.random == 0)
            sTVShowState = 9;
        else if (show->nameRaterShow.random == 1)
            sTVShowState = 10;
        else if (show->nameRaterShow.random == 2)
            sTVShowState = 11;
        break;
    case 9:
    case 10:
    case 11:
        TVShowConvertInternationalString(gStringVar1, show->nameRaterShow.pokemonName, show->nameRaterShow.pokemonNameLanguage);
        GetNicknameSubstring(1, 0, 0, 1, 0, show);
        GetNicknameSubstring(2, 1, 0, 1, 0, show);
        sTVShowState = 12;
        break;
    case 13:
        TVShowConvertInternationalString(gStringVar1, show->nameRaterShow.trainerName, show->nameRaterShow.language);
        GetNicknameSubstring(1, 0, 2, 0, 0, show);
        GetNicknameSubstring(2, 0, 3, 1, 0, show);
        sTVShowState = 14;
        break;
    case 14:
        GetNicknameSubstring(1, 0, 2, 1, 0, show);
        GetNicknameSubstring(2, 0, 3, 0, 0, show);
        sTVShowState = 18;
        break;
    case 15:
        GetNicknameSubstring(0, 0, 2, 1, 0, show);
        StringCopy(gStringVar2, gSpeciesNames[show->nameRaterShow.species]);
        GetNicknameSubstring(2, 0, 3, 2, show->nameRaterShow.species, show);
        sTVShowState = 16;
        break;
    case 16:
        GetNicknameSubstring(0, 0, 2, 2, show->nameRaterShow.species, show);
        GetNicknameSubstring(2, 0, 3, 1, 0, show);
        sTVShowState = 17;
        break;
    case 17:
        GetNicknameSubstring(0, 0, 2, 1, 0, show);
        StringCopy(gStringVar2, gSpeciesNames[show->nameRaterShow.randomSpecies]);
        GetNicknameSubstring(2, 0, 3, 2, show->nameRaterShow.randomSpecies, show);
        sTVShowState = 18;
        break;
    case 12:
        state = 18;
        sTVShowState = 18;
    case 18:
        TVShowConvertInternationalString(gStringVar1, show->nameRaterShow.pokemonName, show->nameRaterShow.pokemonNameLanguage);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVNameRaterTextGroup[state]);
}

static void DoTVShowPokemonTodaySuccessfulCapture(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->pokemonToday.playerName, show->pokemonToday.language);
        StringCopy(gStringVar2, gSpeciesNames[show->pokemonToday.species]);
        TVShowConvertInternationalString(gStringVar3, show->pokemonToday.nickname, show->pokemonToday.language2);
        if (show->pokemonToday.ball == ITEM_MASTER_BALL)
            sTVShowState = 5;
        else
            sTVShowState = 1;
        break;
    case 1:
        sTVShowState = 2;
        break;
    case 2:
        StringCopy(gStringVar2, ItemId_GetName(show->pokemonToday.ball));
        ConvertIntToDecimalString(2, show->pokemonToday.nBallsUsed);
        if (show->pokemonToday.nBallsUsed < 4)
            sTVShowState = 3;
        else
            sTVShowState = 4;
        break;
    case 3:
        TVShowConvertInternationalString(gStringVar1, show->pokemonToday.playerName, show->pokemonToday.language);
        StringCopy(gStringVar2, gSpeciesNames[show->pokemonToday.species]);
        TVShowConvertInternationalString(gStringVar3, show->pokemonToday.nickname, show->pokemonToday.language2);
        sTVShowState = 6;
        break;
    case 4:
        sTVShowState = 6;
        break;
    case 5:
        TVShowConvertInternationalString(gStringVar1, show->pokemonToday.playerName, show->pokemonToday.language);
        StringCopy(gStringVar2, gSpeciesNames[show->pokemonToday.species]);
        sTVShowState = 6;
        break;
    case 6:
        TVShowConvertInternationalString(gStringVar1, show->pokemonToday.playerName, show->pokemonToday.language);
        StringCopy(gStringVar2, gSpeciesNames[show->pokemonToday.species]);
        TVShowConvertInternationalString(gStringVar3, show->pokemonToday.nickname, show->pokemonToday.language2);
        sTVShowState += 1 + (Random() % 4);
        break;
    case 7:
    case 8:
        StringCopy(gStringVar1, gSpeciesNames[show->pokemonToday.species]);
        TVShowConvertInternationalString(gStringVar2, show->pokemonToday.nickname, show->pokemonToday.language2);
        GetRandomDifferentSpeciesAndNameSeenByPlayer(2, show->pokemonToday.species);
        sTVShowState = 11;
        break;
    case 9:
    case 10:
        StringCopy(gStringVar1, gSpeciesNames[show->pokemonToday.species]);
        TVShowConvertInternationalString(gStringVar2, show->pokemonToday.nickname, show->pokemonToday.language2);
        sTVShowState = 11;
        break;
    case 11:
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVPokemonTodaySuccessfulTextGroup[state]);
}

static void DoTVShowPokemonTodayFailedCapture(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->pokemonTodayFailed.playerName, show->pokemonTodayFailed.language);
        StringCopy(gStringVar2, gSpeciesNames[show->pokemonTodayFailed.species]);
        sTVShowState = 1;
        break;
    case 1:
        TVShowConvertInternationalString(gStringVar1, show->pokemonTodayFailed.playerName, show->pokemonTodayFailed.language);
        GetMapName(gStringVar2, show->pokemonTodayFailed.location, 0);
        StringCopy(gStringVar3, gSpeciesNames[show->pokemonTodayFailed.species2]);
        if (show->pokemonTodayFailed.outcome == 1)
            sTVShowState = 3;
        else
            sTVShowState = 2;
        break;
    case 2:
    case 3:
        TVShowConvertInternationalString(gStringVar1, show->pokemonTodayFailed.playerName, show->pokemonTodayFailed.language);
        ConvertIntToDecimalString(1, show->pokemonTodayFailed.nBallsUsed);
        if (Random() % 3 == 0)
            sTVShowState = 5;
        else
            sTVShowState = 4;
        break;
    case 4:
    case 5:
        TVShowConvertInternationalString(gStringVar1, show->pokemonTodayFailed.playerName, show->pokemonTodayFailed.language);
        sTVShowState = 6;
        break;
    case 6:
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVPokemonTodayFailedTextGroup[state]);
}

static void DoTVShowPokemonFanClubLetter(void)
{
    TVShow *show;
    u8 state;
    u16 rval;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->fanclubLetter.playerName, show->fanclubLetter.language);
        StringCopy(gStringVar2, gSpeciesNames[show->fanclubLetter.species]);
        sTVShowState = 50;
        break;
    case 1:
        rval = (Random() % 4) + 1;
        if (rval == 1)
            sTVShowState = 2;
        else
            sTVShowState = rval + 2;
        break;
    case 2:
        sTVShowState = 51;
        break;
    case 3:
        sTVShowState += (Random() % 3) + 1;
        break;
    case 4:
    case 5:
    case 6:
        GetRandomWordFromShow(show);
        sTVShowState = 7;
        break;
    case 7:
        rval = (Random() % 0x1f) + 0x46;
        ConvertIntToDecimalString(2, rval);
        TVShowDone();
        break;
    case 50:
        ConvertEasyChatWordsToString(gStringVar4, show->fanclubLetter.words, 2, 2);
        ShowFieldMessage(gStringVar4);
        sTVShowState = 1;
        return;
    case 51:
        ConvertEasyChatWordsToString(gStringVar4, show->fanclubLetter.words, 2, 2);
        ShowFieldMessage(gStringVar4);
        sTVShowState = 3;
        return;
    }
    ShowFieldMessage(sTVFanClubTextGroup[state]);
}

static void DoTVShowRecentHappenings(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->recentHappenings.playerName, show->recentHappenings.language);
        GetRandomWordFromShow(show);
        sTVShowState = 50;
        break;
    case 1:
        sTVShowState += 1 + (Random() % 3);
        break;
    case 2:
    case 3:
    case 4:
        sTVShowState = 5;
        break;
    case 5:
        TVShowDone();
        break;
    case 50:
        ConvertEasyChatWordsToString(gStringVar4, show->recentHappenings.words, 2, 2);
        ShowFieldMessage(gStringVar4);
        sTVShowState = 1;
        return;
    }
    ShowFieldMessage(sTVRecentHappeninssTextGroup[state]);
}

static void DoTVShowPokemonFanClubOpinions(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->fanclubOpinions.playerName, show->fanclubOpinions.language);
        StringCopy(gStringVar2, gSpeciesNames[show->fanclubOpinions.species]);
        TVShowConvertInternationalString(gStringVar3, show->fanclubOpinions.nickname, show->fanclubOpinions.pokemonNameLanguage);
        sTVShowState = show->fanclubOpinions.questionAsked + 1;
        break;
    case 1:
    case 2:
    case 3:
        TVShowConvertInternationalString(gStringVar1, show->fanclubOpinions.playerName, show->fanclubOpinions.language);
        StringCopy(gStringVar2, gSpeciesNames[show->fanclubOpinions.species]);
        CopyEasyChatWord(gStringVar3, show->fanclubOpinions.words[0]);
        sTVShowState = 4;
        break;
    case 4:
        TVShowConvertInternationalString(gStringVar1, show->fanclubOpinions.playerName, show->fanclubOpinions.language);
        CopyEasyChatWord(gStringVar3, show->fanclubOpinions.words[1]);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVFanClubOpinionsTextGroup[state]);
}

static void DoTVShowDummiedOut(void)
{

}

static void DoTVShowPokemonNewsMassOutbreak(void)
{
    TVShow *show;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    GetMapName(gStringVar1, show->massOutbreak.locationMapNum, 0);
    StringCopy(gStringVar2, gSpeciesNames[show->massOutbreak.species]);
    TVShowDone();
    StartMassOutbreak();
    ShowFieldMessage(sTVMassOutbreakTextGroup[sTVShowState]);
}

// TV Show that plays after a Link Contest.
// First talks about the winner and something they did, then about a losing player and something they did
// The show is only generated when the player wins, but can be record mixed to other games
// Each state buffers any needed data for a message to print from sTVContestLiveUpdatesTextGroup
// Many cases in this function are identical, and its size can be reduced a good deal by collapsing them
// Can't get this to match while collapsing them though
static void DoTVShowPokemonContestLiveUpdates(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case CONTESTLIVE_STATE_INTRO:
        BufferContestName(gStringVar1, show->contestLiveUpdates.category);
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        TVShowConvertInternationalString(gStringVar3, show->contestLiveUpdates.winningTrainerName, show->contestLiveUpdates.winningTrainerLanguage);
        if (show->contestLiveUpdates.round1Placing == show->contestLiveUpdates.round2Placing)
        {
            if (show->contestLiveUpdates.round1Placing == 0)
                sTVShowState = CONTESTLIVE_STATE_WON_BOTH_ROUNDS;
            else
                sTVShowState = CONTESTLIVE_STATE_EQUAL_ROUNDS;
        }
        else if (show->contestLiveUpdates.round1Placing > show->contestLiveUpdates.round2Placing)
        {
            sTVShowState = CONTESTLIVE_STATE_BETTER_ROUND2;
        }
        else
        {
            sTVShowState = CONTESTLIVE_STATE_BETTER_ROUND1;
        }
        break;
    case CONTESTLIVE_STATE_WON_BOTH_ROUNDS:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        switch (show->contestLiveUpdates.winnerAppealFlag)
        {
        case CONTESTLIVE_FLAG_EXCITING_APPEAL:
            sTVShowState = CONTESTLIVE_STATE_EXCITING_APPEAL;
            break;
        case CONTESTLIVE_FLAG_GOT_NERVOUS:
            sTVShowState = CONTESTLIVE_STATE_GOT_NERVOUS;
            break;
        case CONTESTLIVE_FLAG_MAXED_EXCITEMENT:
            sTVShowState = CONTESTLIVE_STATE_VERY_EXCITING_APPEAL;
            break;
        case CONTESTLIVE_FLAG_USED_COMBO:
            sTVShowState = CONTESTLIVE_STATE_USED_COMBO;
            break;
        case CONTESTLIVE_FLAG_STARTLED_OTHER:
            sTVShowState = CONTESTLIVE_STATE_STARTLED_OTHER;
            break;
        case CONTESTLIVE_FLAG_SKIPPED_TURN:
            sTVShowState = CONTESTLIVE_STATE_TOOK_BREAK;
            break;
        case CONTESTLIVE_FLAG_GOT_STARTLED:
            sTVShowState = CONTESTLIVE_STATE_GOT_STARTLED;
            break;
        case CONTESTLIVE_FLAG_MADE_APPEAL:
            sTVShowState = CONTESTLIVE_STATE_USED_MOVE;
            break;
        }
        break;
    case CONTESTLIVE_STATE_BETTER_ROUND2:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        switch (show->contestLiveUpdates.winnerAppealFlag)
        {
        case CONTESTLIVE_FLAG_EXCITING_APPEAL:
            sTVShowState = CONTESTLIVE_STATE_EXCITING_APPEAL;
            break;
        case CONTESTLIVE_FLAG_GOT_NERVOUS:
            sTVShowState = CONTESTLIVE_STATE_GOT_NERVOUS;
            break;
        case CONTESTLIVE_FLAG_MAXED_EXCITEMENT:
            sTVShowState = CONTESTLIVE_STATE_VERY_EXCITING_APPEAL;
            break;
        case CONTESTLIVE_FLAG_USED_COMBO:
            sTVShowState = CONTESTLIVE_STATE_USED_COMBO;
            break;
        case CONTESTLIVE_FLAG_STARTLED_OTHER:
            sTVShowState = CONTESTLIVE_STATE_STARTLED_OTHER;
            break;
        case CONTESTLIVE_FLAG_SKIPPED_TURN:
            sTVShowState = CONTESTLIVE_STATE_TOOK_BREAK;
            break;
        case CONTESTLIVE_FLAG_GOT_STARTLED:
            sTVShowState = CONTESTLIVE_STATE_GOT_STARTLED;
            break;
        case CONTESTLIVE_FLAG_MADE_APPEAL:
            sTVShowState = CONTESTLIVE_STATE_USED_MOVE;
            break;
        }
        break;
    case CONTESTLIVE_STATE_EQUAL_ROUNDS:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        TVShowConvertInternationalString(gStringVar3, show->contestLiveUpdates.winningTrainerName, show->contestLiveUpdates.winningTrainerLanguage);
        switch (show->contestLiveUpdates.winnerAppealFlag)
        {
        case CONTESTLIVE_FLAG_EXCITING_APPEAL:
            sTVShowState = CONTESTLIVE_STATE_EXCITING_APPEAL;
            break;
        case CONTESTLIVE_FLAG_GOT_NERVOUS:
            sTVShowState = CONTESTLIVE_STATE_GOT_NERVOUS;
            break;
        case CONTESTLIVE_FLAG_MAXED_EXCITEMENT:
            sTVShowState = CONTESTLIVE_STATE_VERY_EXCITING_APPEAL;
            break;
        case CONTESTLIVE_FLAG_USED_COMBO:
            sTVShowState = CONTESTLIVE_STATE_USED_COMBO;
            break;
        case CONTESTLIVE_FLAG_STARTLED_OTHER:
            sTVShowState = CONTESTLIVE_STATE_STARTLED_OTHER;
            break;
        case CONTESTLIVE_FLAG_SKIPPED_TURN:
            sTVShowState = CONTESTLIVE_STATE_TOOK_BREAK;
            break;
        case CONTESTLIVE_FLAG_GOT_STARTLED:
            sTVShowState = CONTESTLIVE_STATE_GOT_STARTLED;
            break;
        case CONTESTLIVE_FLAG_MADE_APPEAL:
            sTVShowState = CONTESTLIVE_STATE_USED_MOVE;
            break;
        }
        break;
    case CONTESTLIVE_STATE_BETTER_ROUND1:
        switch (show->contestLiveUpdates.category)
        {
        case CONTEST_CATEGORY_COOL:
            StringCopy(gStringVar1, gText_Cool);
            break;
        case CONTEST_CATEGORY_BEAUTY:
            StringCopy(gStringVar1, gText_Beauty);
            break;
        case CONTEST_CATEGORY_CUTE:
            StringCopy(gStringVar1, gText_Cute);
            break;
        case CONTEST_CATEGORY_SMART:
            StringCopy(gStringVar1, gText_Smart);
            break;
        case CONTEST_CATEGORY_TOUGH:
            StringCopy(gStringVar1, gText_Tough);
            break;
        }
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        switch (show->contestLiveUpdates.winnerAppealFlag)
        {
        case CONTESTLIVE_FLAG_EXCITING_APPEAL:
            sTVShowState = CONTESTLIVE_STATE_EXCITING_APPEAL;
            break;
        case CONTESTLIVE_FLAG_GOT_NERVOUS:
            sTVShowState = CONTESTLIVE_STATE_GOT_NERVOUS;
            break;
        case CONTESTLIVE_FLAG_MAXED_EXCITEMENT:
            sTVShowState = CONTESTLIVE_STATE_VERY_EXCITING_APPEAL;
            break;
        case CONTESTLIVE_FLAG_USED_COMBO:
            sTVShowState = CONTESTLIVE_STATE_USED_COMBO;
            break;
        case CONTESTLIVE_FLAG_STARTLED_OTHER:
            sTVShowState = CONTESTLIVE_STATE_STARTLED_OTHER;
            break;
        case CONTESTLIVE_FLAG_SKIPPED_TURN:
            sTVShowState = CONTESTLIVE_STATE_TOOK_BREAK;
            break;
        case CONTESTLIVE_FLAG_GOT_STARTLED:
            sTVShowState = CONTESTLIVE_STATE_GOT_STARTLED;
            break;
        case CONTESTLIVE_FLAG_MADE_APPEAL:
            sTVShowState = CONTESTLIVE_STATE_USED_MOVE;
            break;
        }
        break;
    case CONTESTLIVE_STATE_GOT_NERVOUS:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_STARTLED_OTHER:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_USED_COMBO:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_EXCITING_APPEAL:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        switch (show->contestLiveUpdates.category)
        {
        case CONTEST_CATEGORY_COOL:
            sTVShowState = CONTESTLIVE_STATE_COOL;
            break;
        case CONTEST_CATEGORY_BEAUTY:
            sTVShowState = CONTESTLIVE_STATE_BEAUTIFUL;
            break;
        case CONTEST_CATEGORY_CUTE:
            sTVShowState = CONTESTLIVE_STATE_CUTE;
            break;
        case CONTEST_CATEGORY_SMART:
            sTVShowState = CONTESTLIVE_STATE_SMART;
            break;
        case CONTEST_CATEGORY_TOUGH:
            sTVShowState = CONTESTLIVE_STATE_TOUGH;
            break;
        }
        break;
    case CONTESTLIVE_STATE_COOL:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_BEAUTIFUL:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_CUTE:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_SMART:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_TOUGH:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_VERY_EXCITING_APPEAL:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        switch (show->contestLiveUpdates.category)
        {
        case CONTEST_CATEGORY_COOL:
            sTVShowState = CONTESTLIVE_STATE_VERY_COOL;
            break;
        case CONTEST_CATEGORY_BEAUTY:
            sTVShowState = CONTESTLIVE_STATE_VERY_BEAUTIFUL;
            break;
        case CONTEST_CATEGORY_CUTE:
            sTVShowState = CONTESTLIVE_STATE_VERY_CUTE;
            break;
        case CONTEST_CATEGORY_SMART:
            sTVShowState = CONTESTLIVE_STATE_VERY_SMART;
            break;
        case CONTEST_CATEGORY_TOUGH:
            sTVShowState = CONTESTLIVE_STATE_VERY_TOUGH;
            break;
        }
        break;
    case CONTESTLIVE_STATE_VERY_COOL:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_VERY_BEAUTIFUL:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_VERY_CUTE:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_VERY_SMART:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_VERY_TOUGH:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_TOOK_BREAK:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_GOT_STARTLED:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_USED_MOVE:
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        StringCopy(gStringVar3, gMoveNames[show->contestLiveUpdates.move]);
        sTVShowState = CONTESTLIVE_STATE_TALK_ABOUT_LOSER;
        break;
    case CONTESTLIVE_STATE_TALK_ABOUT_LOSER:
        StringCopy(gStringVar1, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        TVShowConvertInternationalString(gStringVar2, show->contestLiveUpdates.losingTrainerName, show->contestLiveUpdates.losingTrainerLanguage);
        StringCopy(gStringVar3, gSpeciesNames[show->contestLiveUpdates.losingSpecies]);
        switch (show->contestLiveUpdates.loserAppealFlag)
        {
        case CONTESTLIVE_FLAG_LOST:
            sTVShowState = CONTESTLIVE_STATE_LOST;
            break;
        case CONTESTLIVE_FLAG_REPEATED_MOVE:
            sTVShowState = CONTESTLIVE_STATE_REPEATED_APPEALS;
            break;
        case CONTESTLIVE_FLAG_LOST_SMALL_MARGIN:
            sTVShowState = CONTESTLIVE_STATE_LOST_SMALL_MARGIN;
            break;
        case CONTESTLIVE_FLAG_NO_EXCITEMENT:
            sTVShowState = CONTESTLIVE_STATE_NO_EXCITING_APPEALS;
            break;
        case CONTESTLIVE_FLAG_BLEW_LEAD:
            sTVShowState = CONTESTLIVE_STATE_LOST_AFTER_ROUND1_WIN;
            break;
        case CONTESTLIVE_FLAG_MISSED_EXCITEMENT:
            sTVShowState = CONTESTLIVE_STATE_NOT_EXCITING_ENOUGH;
            break;
        case CONTESTLIVE_FLAG_LAST_BOTH_ROUNDS:
            sTVShowState = CONTESTLIVE_STATE_LAST_BOTH;
            break;
        case CONTESTLIVE_FLAG_NO_APPEALS:
            sTVShowState = CONTESTLIVE_STATE_NO_APPEALS;
            break;
        }
        break;
    case CONTESTLIVE_STATE_NO_APPEALS:
        StringCopy(gStringVar1, gSpeciesNames[show->contestLiveUpdates.losingSpecies]);
        sTVShowState = CONTESTLIVE_STATE_OUTRO;
        break;
    case CONTESTLIVE_STATE_LAST_BOTH:
        TVShowConvertInternationalString(gStringVar1, show->contestLiveUpdates.losingTrainerName, show->contestLiveUpdates.losingTrainerLanguage);
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.losingSpecies]);
        sTVShowState = CONTESTLIVE_STATE_OUTRO;
        break;
    case CONTESTLIVE_STATE_NO_EXCITING_APPEALS:
        sTVShowState = CONTESTLIVE_STATE_OUTRO;
        break;
    case CONTESTLIVE_STATE_LOST_SMALL_MARGIN:
        TVShowConvertInternationalString(gStringVar1, show->contestLiveUpdates.winningTrainerName, show->contestLiveUpdates.winningTrainerLanguage);
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        TVShowConvertInternationalString(gStringVar3, show->contestLiveUpdates.losingTrainerName, show->contestLiveUpdates.losingTrainerLanguage);
        sTVShowState = CONTESTLIVE_STATE_OUTRO;
        break;
    case CONTESTLIVE_STATE_NOT_EXCITING_ENOUGH:
    case CONTESTLIVE_STATE_LOST_AFTER_ROUND1_WIN:
    case CONTESTLIVE_STATE_REPEATED_APPEALS:
    case CONTESTLIVE_STATE_LOST:
        TVShowConvertInternationalString(gStringVar1, show->contestLiveUpdates.losingTrainerName, show->contestLiveUpdates.losingTrainerLanguage);
        sTVShowState = CONTESTLIVE_STATE_OUTRO;
        break;
    case CONTESTLIVE_STATE_OUTRO:
        TVShowConvertInternationalString(gStringVar1, show->contestLiveUpdates.winningTrainerName, show->contestLiveUpdates.winningTrainerLanguage);
        StringCopy(gStringVar2, gSpeciesNames[show->contestLiveUpdates.winningSpecies]);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVContestLiveUpdatesTextGroup[state]);
}

static void DoTVShowPokemonBattleUpdate(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        switch (show->battleUpdate.battleType)
        {
        case 0:
        case 1:
            sTVShowState = 1;
            break;
        case 2:
            sTVShowState = 5;
            break;
        }
        break;
    case 1:
        TVShowConvertInternationalString(gStringVar1, show->battleUpdate.playerName, show->battleUpdate.language);
        TVShowConvertInternationalString(gStringVar2, show->battleUpdate.linkOpponentName, show->battleUpdate.linkOpponentLanguage);
        if (show->battleUpdate.battleType == 0)
        {
            StringCopy(gStringVar3, gText_Single);
        }
        else
        {
            StringCopy(gStringVar3, gText_Double);
        }
        sTVShowState = 2;
        break;
    case 2:
        TVShowConvertInternationalString(gStringVar1, show->battleUpdate.playerName, show->battleUpdate.language);
        StringCopy(gStringVar2, gSpeciesNames[show->battleUpdate.speciesPlayer]);
        StringCopy(gStringVar3, gMoveNames[show->battleUpdate.move]);
        sTVShowState = 3;
        break;
    case 3:
        TVShowConvertInternationalString(gStringVar1, show->battleUpdate.linkOpponentName, show->battleUpdate.linkOpponentLanguage);
        StringCopy(gStringVar2, gSpeciesNames[show->battleUpdate.speciesOpponent]);
        sTVShowState = 4;
        break;
    case 4:
        TVShowConvertInternationalString(gStringVar1, show->battleUpdate.playerName, show->battleUpdate.language);
        TVShowConvertInternationalString(gStringVar2, show->battleUpdate.linkOpponentName, show->battleUpdate.linkOpponentLanguage);
        TVShowDone();
        break;
    case 5:
        TVShowConvertInternationalString(gStringVar1, show->battleUpdate.playerName, show->battleUpdate.language);
        TVShowConvertInternationalString(gStringVar2, show->battleUpdate.linkOpponentName, show->battleUpdate.linkOpponentLanguage);
        sTVShowState = 6;
        break;
    case 6:
        TVShowConvertInternationalString(gStringVar1, show->battleUpdate.playerName, show->battleUpdate.language);
        StringCopy(gStringVar2, gSpeciesNames[show->battleUpdate.speciesPlayer]);
        StringCopy(gStringVar3, gMoveNames[show->battleUpdate.move]);
        sTVShowState = 7;
        break;
    case 7:
        TVShowConvertInternationalString(gStringVar1, show->battleUpdate.playerName, show->battleUpdate.language);
        TVShowConvertInternationalString(gStringVar2, show->battleUpdate.linkOpponentName, show->battleUpdate.linkOpponentLanguage);
        StringCopy(gStringVar3, gSpeciesNames[show->battleUpdate.speciesOpponent]);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVPokemonBattleUpdateTextGroup[state]);
}

static void DoTVShow3CheersForPokeblocks(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->threeCheers.playerName, show->threeCheers.language);
        if (show->threeCheers.sheen > 20)
            sTVShowState = 1;
        else
            sTVShowState = 3;
        break;
    case 1:
        switch (show->threeCheers.flavor)
        {
        case 0:
            StringCopy(gStringVar1, gText_Spicy2);
            break;
        case 1:
            StringCopy(gStringVar1, gText_Dry2);
            break;
        case 2:
            StringCopy(gStringVar1, gText_Sweet2);
            break;
        case 3:
            StringCopy(gStringVar1, gText_Bitter2);
            break;
        case 4:
            StringCopy(gStringVar1, gText_Sour2);
            break;
        }
        if (show->threeCheers.sheen > 24)
        {
            StringCopy(gStringVar2, gText_Excellent);
        } else if (show->threeCheers.sheen > 22)
        {
            StringCopy(gStringVar2, gText_VeryGood);
        }
        else
        {
            StringCopy(gStringVar2, gText_Good);
        }
        TVShowConvertInternationalString(gStringVar3, show->threeCheers.playerName, show->threeCheers.language);
        sTVShowState = 2;
        break;
    case 2:
        TVShowConvertInternationalString(gStringVar1, show->threeCheers.worstBlenderName, show->threeCheers.worstBlenderLanguage);
        sTVShowState = 5;
        break;
    case 3:
        switch (show->threeCheers.flavor)
        {
        case 0:
            StringCopy(gStringVar1, gText_Spicy2);
            break;
        case 1:
            StringCopy(gStringVar1, gText_Dry2);
            break;
        case 2:
            StringCopy(gStringVar1, gText_Sweet2);
            break;
        case 3:
            StringCopy(gStringVar1, gText_Bitter2);
            break;
        case 4:
            StringCopy(gStringVar1, gText_Sour2);
            break;
        }

        if (show->threeCheers.sheen > 16)
            StringCopy(gStringVar2, gText_SoSo);
        else if (show->threeCheers.sheen > 13)
            StringCopy(gStringVar2, gText_Bad);
        else
            StringCopy(gStringVar2, gText_TheWorst);

        TVShowConvertInternationalString(gStringVar3, show->threeCheers.playerName, show->threeCheers.language);
        sTVShowState = 4;
        break;
    case 4:
        TVShowConvertInternationalString(gStringVar1, show->threeCheers.worstBlenderName, show->threeCheers.worstBlenderLanguage);
        TVShowConvertInternationalString(gStringVar2, show->threeCheers.playerName, show->threeCheers.language);
        sTVShowState = 5;
        break;
    case 5:
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTV3CheersForPokeblocksTextGroup[state]);
}

void DoTVShowInSearchOfTrainers(void)
{
    u8 state;

    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        GetMapName(gStringVar1, gSaveBlock1Ptr->gabbyAndTyData.mapnum, 0);
        if (gSaveBlock1Ptr->gabbyAndTyData.battleNum > 1)
            sTVShowState = 1;
        else
            sTVShowState = 2;
        break;
    case 1:
        sTVShowState = 2;
        break;
    case 2:
        if (!gSaveBlock1Ptr->gabbyAndTyData.battleTookMoreThanOneTurn)
            sTVShowState = 4;
        else if (gSaveBlock1Ptr->gabbyAndTyData.playerThrewABall)
            sTVShowState = 5;
        else if (gSaveBlock1Ptr->gabbyAndTyData.playerUsedHealingItem)
            sTVShowState = 6;
        else if (gSaveBlock1Ptr->gabbyAndTyData.playerLostAMon)
            sTVShowState = 7;
        else
            sTVShowState = 3;
        break;
    case 3:
        StringCopy(gStringVar1, gSpeciesNames[gSaveBlock1Ptr->gabbyAndTyData.mon1]);
        StringCopy(gStringVar2, gMoveNames[gSaveBlock1Ptr->gabbyAndTyData.lastMove]);
        StringCopy(gStringVar3, gSpeciesNames[gSaveBlock1Ptr->gabbyAndTyData.mon2]);
        sTVShowState = 8;
        break;
    case 4:
    case 5:
    case 6:
    case 7:
        sTVShowState = 8;
        break;
    case 8:
        CopyEasyChatWord(gStringVar1, gSaveBlock1Ptr->gabbyAndTyData.quote[0]);
        StringCopy(gStringVar2, gSpeciesNames[gSaveBlock1Ptr->gabbyAndTyData.mon1]);
        StringCopy(gStringVar3, gSpeciesNames[gSaveBlock1Ptr->gabbyAndTyData.mon2]);
        gSpecialVar_Result = TRUE;
        sTVShowState = 0;
        TakeGabbyAndTyOffTheAir();
        break;
    }
    ShowFieldMessage(sTVInSearchOfTrainersTextGroup[state]);
}

static void DoTVShowPokemonAngler(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    if (show->pokemonAngler.nBites < show->pokemonAngler.nFails)
        sTVShowState = 0;
    else
        sTVShowState = 1;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->pokemonAngler.playerName, show->pokemonAngler.language);
        StringCopy(gStringVar2, gSpeciesNames[show->pokemonAngler.species]);
        ConvertIntToDecimalString(2, show->pokemonAngler.nFails);
        TVShowDone();
        break;
    case 1:
        TVShowConvertInternationalString(gStringVar1, show->pokemonAngler.playerName, show->pokemonAngler.language);
        StringCopy(gStringVar2, gSpeciesNames[show->pokemonAngler.species]);
        ConvertIntToDecimalString(2, show->pokemonAngler.nBites);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVPokemonAnslerTextGroup[state]);
}

static void DoTVShowTheWorldOfMasters(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->worldOfMasters.playerName, show->worldOfMasters.language);
        ConvertIntToDecimalString(1, show->worldOfMasters.steps);
        ConvertIntToDecimalString(2, show->worldOfMasters.numPokeCaught);
        sTVShowState = 1;
        break;
    case 1:
        StringCopy(gStringVar1, gSpeciesNames[show->worldOfMasters.species]);
        sTVShowState = 2;
        break;
    case 2:
        TVShowConvertInternationalString(gStringVar1, show->worldOfMasters.playerName, show->worldOfMasters.language);
        GetMapName(gStringVar2, show->worldOfMasters.location, 0);
        StringCopy(gStringVar3, gSpeciesNames[show->worldOfMasters.caughtPoke]);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVWorldOfMastersTextGroup[state]);
}

static void DoTVShowTodaysRivalTrainer(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        switch (show->rivalTrainer.location)
        {
        default:
            sTVShowState = 7;
            break;
        case MAPSEC_SECRET_BASE:
            sTVShowState = 8;
            break;
        case MAPSEC_DYNAMIC:
            switch (show->rivalTrainer.mapLayoutId)
            {
            case LAYOUT_SS_TIDAL_CORRIDOR:
            case LAYOUT_SS_TIDAL_LOWER_DECK:
            case LAYOUT_SS_TIDAL_ROOMS:
                sTVShowState = 10;
                break;
            default:
                sTVShowState = 9;
                break;
            }
            break;
        }
        break;
    case 7:
        TVShowConvertInternationalString(gStringVar1, show->rivalTrainer.playerName, show->rivalTrainer.language);
        ConvertIntToDecimalString(1, show->rivalTrainer.dexCount);
        GetMapName(gStringVar3, show->rivalTrainer.location, 0);
        if (show->rivalTrainer.badgeCount != 0)
            sTVShowState = 1;
        else
            sTVShowState = 2;
        break;
    case 8:
        TVShowConvertInternationalString(gStringVar1, show->rivalTrainer.playerName, show->rivalTrainer.language);
        ConvertIntToDecimalString(1, show->rivalTrainer.dexCount);
        if (show->rivalTrainer.badgeCount != 0)
            sTVShowState = 1;
        else
            sTVShowState = 2;
        break;
    case 9:
        TVShowConvertInternationalString(gStringVar1, show->rivalTrainer.playerName, show->rivalTrainer.language);
        ConvertIntToDecimalString(1, show->rivalTrainer.dexCount);
        if (show->rivalTrainer.badgeCount != 0)
            sTVShowState = 1;
        else
            sTVShowState = 2;
        break;
    case 10:
        TVShowConvertInternationalString(gStringVar1, show->rivalTrainer.playerName, show->rivalTrainer.language);
        ConvertIntToDecimalString(1, show->rivalTrainer.dexCount);
        if (show->rivalTrainer.badgeCount != 0)
            sTVShowState = 1;
        else
            sTVShowState = 2;
        break;
    case 1:
        ConvertIntToDecimalString(0, show->rivalTrainer.badgeCount);
        if (FlagGet(FLAG_LANDMARK_BATTLE_FRONTIER))
        {
            if (show->rivalTrainer.nSilverSymbols || show->rivalTrainer.nGoldSymbols)
                sTVShowState = 4;
            else
                sTVShowState = 3;
        }
        else
        {
            sTVShowState = 6;
        }
        break;
    case 2:
        if (FlagGet(FLAG_LANDMARK_BATTLE_FRONTIER))
        {
            if (show->rivalTrainer.nSilverSymbols || show->rivalTrainer.nGoldSymbols)
                sTVShowState = 4;
            else
                sTVShowState = 3;
        }
        else
        {
            sTVShowState = 6;
        }
        break;
    case 3:
        if (show->rivalTrainer.battlePoints == 0)
            sTVShowState = 6;
        else
            sTVShowState = 5;
        break;
    case 4:
        ConvertIntToDecimalString(0, show->rivalTrainer.nGoldSymbols);
        ConvertIntToDecimalString(1, show->rivalTrainer.nSilverSymbols);
        if (show->rivalTrainer.battlePoints == 0)
            sTVShowState = 6;
        else
            sTVShowState = 5;
        break;
    case 5:
        ConvertIntToDecimalString(0, show->rivalTrainer.battlePoints);
        sTVShowState = 6;
        break;
    case 6:
        TVShowConvertInternationalString(gStringVar1, show->rivalTrainer.playerName, show->rivalTrainer.language);
        TVShowDone();
    }
    ShowFieldMessage(sTVTodaysRivalTrainerTextGroup[state]);
}

static void DoTVShowDewfordTrendWatcherNetwork(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case TRENDWATCHER_STATE_INTRO:
        CopyEasyChatWord(gStringVar1, show->trendWatcher.words[0]);
        CopyEasyChatWord(gStringVar2, show->trendWatcher.words[1]);
        if (show->trendWatcher.gender == MALE)
            sTVShowState = TRENDWATCHER_STATE_TAUGHT_MALE;
        else
            sTVShowState = TRENDWATCHER_STATE_TAUGHT_FEMALE;
        break;
    case TRENDWATCHER_STATE_TAUGHT_MALE:
    case TRENDWATCHER_STATE_TAUGHT_FEMALE:
        CopyEasyChatWord(gStringVar1, show->trendWatcher.words[0]);
        CopyEasyChatWord(gStringVar2, show->trendWatcher.words[1]);
        TVShowConvertInternationalString(gStringVar3, show->trendWatcher.playerName, show->trendWatcher.language);
        sTVShowState = TRENDWATCHER_STATE_PHRASE_HOPELESS;
        break;
    case TRENDWATCHER_STATE_PHRASE_HOPELESS:
        CopyEasyChatWord(gStringVar1, show->trendWatcher.words[0]);
        CopyEasyChatWord(gStringVar2, show->trendWatcher.words[1]);
        if (show->trendWatcher.gender == MALE)
            sTVShowState = TRENDWATCHER_STATE_BIGGER_MALE;
        else
            sTVShowState = TRENDWATCHER_STATE_BIGGER_FEMALE;
        break;
    case TRENDWATCHER_STATE_BIGGER_MALE:
    case TRENDWATCHER_STATE_BIGGER_FEMALE:
        CopyEasyChatWord(gStringVar1, show->trendWatcher.words[0]);
        CopyEasyChatWord(gStringVar2, show->trendWatcher.words[1]);
        TVShowConvertInternationalString(gStringVar3, show->trendWatcher.playerName, show->trendWatcher.language);
        sTVShowState = TRENDWATCHER_STATE_OUTRO;
        break;
    case TRENDWATCHER_STATE_OUTRO:
        CopyEasyChatWord(gStringVar1, show->trendWatcher.words[0]);
        CopyEasyChatWord(gStringVar2, show->trendWatcher.words[1]);
        TVShowDone();
    }
    ShowFieldMessage(sTVDewfordTrendWatcherNetworkTextGroup[state]);
}

static void DoTVShowHoennTreasureInvestigators(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        StringCopy(gStringVar1, ItemId_GetName(show->treasureInvestigators.item));
        if (show->treasureInvestigators.location == MAPSEC_DYNAMIC)
        {
            switch (show->treasureInvestigators.mapLayoutId)
            {
            case LAYOUT_SS_TIDAL_CORRIDOR:
            case LAYOUT_SS_TIDAL_LOWER_DECK:
            case LAYOUT_SS_TIDAL_ROOMS:
                sTVShowState = 2;
                break;
            default:
                sTVShowState = 1;
                break;
            }
        }
        else
        {
            sTVShowState = 1;
        }
        break;
    case 1:
        StringCopy(gStringVar1, ItemId_GetName(show->treasureInvestigators.item));
        TVShowConvertInternationalString(gStringVar2, show->treasureInvestigators.playerName, show->treasureInvestigators.language);
        GetMapName(gStringVar3, show->treasureInvestigators.location, 0);
        TVShowDone();
        break;
    case 2:
        StringCopy(gStringVar1, ItemId_GetName(show->treasureInvestigators.item));
        TVShowConvertInternationalString(gStringVar2, show->treasureInvestigators.playerName, show->treasureInvestigators.language);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVHoennTreasureInvestisatorsTextGroup[state]);
}

static void DoTVShowFindThatGamer(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->findThatGamer.playerName, show->findThatGamer.language);
        switch (show->findThatGamer.whichGame)
        {
        case 0:
            StringCopy(gStringVar2, gText_Slots);
            break;
        case 1:
            StringCopy(gStringVar2, gText_Roulette);
            break;
        }
        if (show->findThatGamer.won == TRUE)
            sTVShowState = 1;
        else
            sTVShowState = 2;
        break;
    case 1:
        TVShowConvertInternationalString(gStringVar1, show->findThatGamer.playerName, show->findThatGamer.language);
        switch (show->findThatGamer.whichGame)
        {
        case 0:
            StringCopy(gStringVar2, gText_Slots);
            break;
        case 1:
            StringCopy(gStringVar2, gText_Roulette);
            break;
        }
        ConvertIntToDecimalString(2, show->findThatGamer.nCoins);
        TVShowDone(); break;
    case 2:
        TVShowConvertInternationalString(gStringVar1, show->findThatGamer.playerName, show->findThatGamer.language);
        switch (show->findThatGamer.whichGame)
        {
        case 0:
            StringCopy(gStringVar2, gText_Slots);
            break;
        case 1:
            StringCopy(gStringVar2, gText_Roulette);
            break;
        }
        ConvertIntToDecimalString(2, show->findThatGamer.nCoins);
        sTVShowState = 3;
        break;
    case 3:
        TVShowConvertInternationalString(gStringVar1, show->findThatGamer.playerName, show->findThatGamer.language);
        switch (show->findThatGamer.whichGame)
        {
        case 0:
            StringCopy(gStringVar2, gText_Roulette);
            break;
        case 1:
            StringCopy(gStringVar2, gText_Slots);
            break;
        }
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVFindThatGamerTextGroup[state]);
}

static void DoTVShowBreakingNewsTV(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        if (show->breakingNews.outcome == 0)
            sTVShowState = 1;
        else
            sTVShowState = 5;
        break;
    case 1:
        TVShowConvertInternationalString(gStringVar1, show->breakingNews.playerName, show->breakingNews.language);
        StringCopy(gStringVar2, gSpeciesNames[show->breakingNews.lastOpponentSpecies]);
        GetMapName(gStringVar3, show->breakingNews.location, 0);
        sTVShowState = 2;
        break;
    case 2:
        TVShowConvertInternationalString(gStringVar1, show->breakingNews.playerName, show->breakingNews.language);
        StringCopy(gStringVar2, gSpeciesNames[show->breakingNews.lastOpponentSpecies]);
        StringCopy(gStringVar3, gSpeciesNames[show->breakingNews.poke1Species]);
        sTVShowState = 3;
        break;
    case 3:
        ConvertIntToDecimalString(0, show->breakingNews.balls);
        StringCopy(gStringVar2, ItemId_GetName(show->breakingNews.caughtMonBall));
        sTVShowState = 4;
        break;
    case 4:
        TVShowConvertInternationalString(gStringVar1, show->breakingNews.playerName, show->breakingNews.language);
        GetMapName(gStringVar2, show->breakingNews.location, 0);
        TVShowDone();
        break;
    case 5:
        TVShowConvertInternationalString(gStringVar1, show->breakingNews.playerName, show->breakingNews.language);
        StringCopy(gStringVar2, gSpeciesNames[show->breakingNews.lastOpponentSpecies]);
        GetMapName(gStringVar3, show->breakingNews.location, 0);
        sTVShowState = 6;
        break;
    case 6:
        TVShowConvertInternationalString(gStringVar1, show->breakingNews.playerName, show->breakingNews.language);
        StringCopy(gStringVar2, gSpeciesNames[show->breakingNews.lastOpponentSpecies]);
        StringCopy(gStringVar3, gSpeciesNames[show->breakingNews.poke1Species]);
        switch (show->breakingNews.outcome)
        {
        case 1:
            if (show->breakingNews.lastUsedMove == MOVE_NONE)
                sTVShowState = 12;
            else
                sTVShowState = 7;
            break;
        case 2:
            sTVShowState = 9;
            break;
        case 3:
            sTVShowState = 10;
            break;
        }
        break;
    case 7:
        StringCopy(gStringVar1, gMoveNames[show->breakingNews.lastUsedMove]);
        StringCopy(gStringVar2, gSpeciesNames[show->breakingNews.poke1Species]);
        sTVShowState = 8;
        break;
    case 12:
        TVShowConvertInternationalString(gStringVar1, show->breakingNews.playerName, show->breakingNews.language);
        StringCopy(gStringVar2, gSpeciesNames[show->breakingNews.lastOpponentSpecies]);
        StringCopy(gStringVar3, gSpeciesNames[show->breakingNews.poke1Species]);
        sTVShowState = 8;
        break;
    case 8:
        TVShowConvertInternationalString(gStringVar1, show->breakingNews.playerName, show->breakingNews.language);
        GetMapName(gStringVar2, show->breakingNews.location, 0);
        sTVShowState = 11;
        break;
    case 9:
    case 10:
        TVShowConvertInternationalString(gStringVar1, show->breakingNews.playerName, show->breakingNews.language);
        StringCopy(gStringVar2, gSpeciesNames[show->breakingNews.lastOpponentSpecies]);
        GetMapName(gStringVar3, show->breakingNews.location, 0);
        sTVShowState = 11;
        break;
    case 11:
        TVShowConvertInternationalString(gStringVar1, show->breakingNews.playerName, show->breakingNews.language);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVBreakingNewsTextGroup[state]);
}

static void DoTVShowSecretBaseVisit(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->secretBaseVisit.playerName, show->secretBaseVisit.language);
        if (show->secretBaseVisit.numDecorations == 0)
            sTVShowState = 2;
        else
            sTVShowState = 1;
        break;
    case 1:
        StringCopy(gStringVar2, gDecorations[show->secretBaseVisit.decorations[0]].name);
        if (show->secretBaseVisit.numDecorations == 1)
            sTVShowState = 4;
        else
            sTVShowState = 3;
        break;
    case 3:
        StringCopy(gStringVar2, gDecorations[show->secretBaseVisit.decorations[1]].name);
        switch (show->secretBaseVisit.numDecorations)
        {
        case 2:
            sTVShowState = 7;
            break;
        case 3:
            sTVShowState = 6;
            break;
        case 4:
            sTVShowState = 5;
            break;
        }
        break;
    case 5:
        StringCopy(gStringVar2, gDecorations[show->secretBaseVisit.decorations[2]].name);
        StringCopy(gStringVar3, gDecorations[show->secretBaseVisit.decorations[3]].name);
        sTVShowState = 8;
        break;
    case 6:
        StringCopy(gStringVar2, gDecorations[show->secretBaseVisit.decorations[2]].name);
        sTVShowState = 8;
        break;
    case 2:
    case 4:
    case 7:
        sTVShowState = 8;
        break;
    case 8:
        TVShowConvertInternationalString(gStringVar1, show->secretBaseVisit.playerName, show->secretBaseVisit.language);
        if (show->secretBaseVisit.avgLevel < 25)
            sTVShowState = 12;
        else if (show->secretBaseVisit.avgLevel < 50)
            sTVShowState = 11;
        else if (show->secretBaseVisit.avgLevel < 70)
            sTVShowState = 10;
        else
            sTVShowState = 9;
        break;
    case 9:
    case 10:
    case 11:
    case 12:
        TVShowConvertInternationalString(gStringVar1, show->secretBaseVisit.playerName, show->secretBaseVisit.language);
        StringCopy(gStringVar2, gSpeciesNames[show->secretBaseVisit.species]);
        StringCopy(gStringVar3, gMoveNames[show->secretBaseVisit.move]);
        sTVShowState = 13;
        break;
    case 13:
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVSecretBaseVisitTextGroup[state]);
}

static void DoTVShowPokemonLotteryWinnerFlashReport(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    TVShowConvertInternationalString(gStringVar1, show->lottoWinner.playerName, show->lottoWinner.language);
    if (show->lottoWinner.whichPrize == 0)
        StringCopy(gStringVar2, gText_Jackpot);
    else if (show->lottoWinner.whichPrize == 1)
        StringCopy(gStringVar2, gText_First);
    else if (show->lottoWinner.whichPrize == 2)
        StringCopy(gStringVar2, gText_Second);
    else
        StringCopy(gStringVar2, gText_Third);
    StringCopy(gStringVar3, ItemId_GetName(show->lottoWinner.item));
    TVShowDone();
    ShowFieldMessage(sTVPokemonLotteryWinnerFlashReportTextGroup[state]);
}

static void DoTVShowThePokemonBattleSeminar(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->battleSeminar.playerName, show->battleSeminar.language);
        StringCopy(gStringVar2, gSpeciesNames[show->battleSeminar.species]);
        StringCopy(gStringVar3, gSpeciesNames[show->battleSeminar.foeSpecies]);
        sTVShowState = 1;
        break;
    case 1:
        TVShowConvertInternationalString(gStringVar1, show->battleSeminar.playerName, show->battleSeminar.language);
        StringCopy(gStringVar2, gSpeciesNames[show->battleSeminar.foeSpecies]);
        StringCopy(gStringVar3, gMoveNames[show->battleSeminar.move]);
        sTVShowState = 2;
        break;
    case 2:
        StringCopy(gStringVar1, gSpeciesNames[show->battleSeminar.species]);
        switch (show->battleSeminar.nOtherMoves)
        {
        case 1:
            sTVShowState = 5;
            break;
        case 2:
            sTVShowState = 4;
            break;
        case 3:
            sTVShowState = 3;
            break;
        default:
            sTVShowState = 6;
            break;
        }
        break;
    case 3:
        StringCopy(gStringVar1, gMoveNames[show->battleSeminar.otherMoves[0]]);
        StringCopy(gStringVar2, gMoveNames[show->battleSeminar.otherMoves[1]]);
        StringCopy(gStringVar3, gMoveNames[show->battleSeminar.otherMoves[2]]);
        sTVShowState = 6;
        break;
    case 4:
        StringCopy(gStringVar1, gMoveNames[show->battleSeminar.otherMoves[0]]);
        StringCopy(gStringVar2, gMoveNames[show->battleSeminar.otherMoves[1]]);
        sTVShowState = 6;
        break;
    case 5:
        StringCopy(gStringVar2, gMoveNames[show->battleSeminar.otherMoves[0]]);
        sTVShowState = 6;
        break;
    case 6:
        StringCopy(gStringVar1, gMoveNames[show->battleSeminar.betterMove]);
        StringCopy(gStringVar2, gMoveNames[show->battleSeminar.move]);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVThePokemonBattleSeminarTextGroup[state]);
}

static void DoTVShowTrainerFanClubSpecial(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->fanClubSpecial.idolName, show->fanClubSpecial.idolNameLanguage);
        TVShowConvertInternationalString(gStringVar2, show->fanClubSpecial.playerName, show->fanClubSpecial.language);
        CopyEasyChatWord(gStringVar3, show->fanClubSpecial.words[0]);
        if (show->fanClubSpecial.score >= 90)
            sTVShowState = 1;
        else if (show->fanClubSpecial.score >= 70)
            sTVShowState = 2;
        else if (show->fanClubSpecial.score >= 30)
            sTVShowState = 3;
        else
            sTVShowState = 4;
        break;
    case 1:
        TVShowConvertInternationalString(gStringVar1, show->fanClubSpecial.idolName, show->fanClubSpecial.idolNameLanguage);
        TVShowConvertInternationalString(gStringVar2, show->fanClubSpecial.playerName, show->fanClubSpecial.language);
        ConvertIntToDecimalString(2, show->fanClubSpecial.score);
        sTVShowState = 5;
        break;
    case 2:
        TVShowConvertInternationalString(gStringVar1, show->fanClubSpecial.idolName, show->fanClubSpecial.idolNameLanguage);
        TVShowConvertInternationalString(gStringVar2, show->fanClubSpecial.playerName, show->fanClubSpecial.language);
        ConvertIntToDecimalString(2, show->fanClubSpecial.score);
        sTVShowState = 5;
        break;
    case 3:
        TVShowConvertInternationalString(gStringVar1, show->fanClubSpecial.idolName, show->fanClubSpecial.idolNameLanguage);
        TVShowConvertInternationalString(gStringVar2, show->fanClubSpecial.playerName, show->fanClubSpecial.language);
        ConvertIntToDecimalString(2, show->fanClubSpecial.score);
        sTVShowState = 5;
        break;
    case 4:
        TVShowConvertInternationalString(gStringVar1, show->fanClubSpecial.idolName, show->fanClubSpecial.idolNameLanguage);
        TVShowConvertInternationalString(gStringVar2, show->fanClubSpecial.playerName, show->fanClubSpecial.language);
        ConvertIntToDecimalString(2, show->fanClubSpecial.score);
        sTVShowState = 5;
        break;
    case 5:
        TVShowConvertInternationalString(gStringVar1, show->fanClubSpecial.idolName, show->fanClubSpecial.idolNameLanguage);
        TVShowConvertInternationalString(gStringVar2, show->fanClubSpecial.playerName, show->fanClubSpecial.language);
        CopyEasyChatWord(gStringVar3, show->fanClubSpecial.words[0]);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVTrainerFanClubSpecialTextGroup[state]);
}

static void DoTVShowTrainerFanClub(void)
{
    TVShow *show;
    u8 state;
    u32 playerId;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->trainerFanClub.playerName, show->trainerFanClub.language);
        playerId = ((show->common.trainerIdHi << 8) + show->common.trainerIdLo);
        switch (playerId % 10)
        {
        case 0:
            sTVShowState = 1;
            break;
        case 1:
            sTVShowState = 2;
            break;
        case 2:
            sTVShowState = 3;
            break;
        case 3:
            sTVShowState = 4;
            break;
        case 4:
            sTVShowState = 5;
            break;
        case 5:
            sTVShowState = 6;
            break;
        case 6:
            sTVShowState = 7;
            break;
        case 7:
            sTVShowState = 8;
            break;
        case 8:
            sTVShowState = 9;
            break;
        case 9:
            sTVShowState = 10;
            break;
        }
        break;
    case 1:
        sTVShowState = 11;
        break;
    case 2:
        sTVShowState = 11;
        break;
    case 3:
        sTVShowState = 11;
        break;
    case 4:
        sTVShowState = 11;
        break;
    case 5:
        sTVShowState = 11;
        break;
    case 6:
        sTVShowState = 11;
        break;
    case 7:
        sTVShowState = 11;
        break;
    case 8:
        sTVShowState = 11;
        break;
    case 9:
        sTVShowState = 11;
        break;
    case 10:
        sTVShowState = 11;
        break;
    case 11:
        TVShowConvertInternationalString(gStringVar1, show->trainerFanClub.playerName, show->trainerFanClub.language);
        CopyEasyChatWord(gStringVar2, show->trainerFanClub.words[0]);
        CopyEasyChatWord(gStringVar3, show->trainerFanClub.words[1]);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVTrainerFanClubTextGroup[state]);
}

static void DoTVShowSpotTheCuties(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;

    // For each state, in addition to the switch a message
    // is printed from the table at the bottom
    state = sTVShowState;
    switch (state)
    {
    case SPOTCUTIES_STATE_INTRO:
        TVShowConvertInternationalString(gStringVar1, show->cuties.playerName, show->cuties.language);
        TVShowConvertInternationalString(gStringVar2, show->cuties.nickname, show->cuties.pokemonNameLanguage);

        // Comments following the intro depend on how many ribbons the pokemon has
        if (show->cuties.nRibbons < 10)
            sTVShowState = SPOTCUTIES_STATE_RIBBONS_LOW;
        else if (show->cuties.nRibbons < 20)
            sTVShowState = SPOTCUTIES_STATE_RIBBONS_MID;
        else
            sTVShowState = SPOTCUTIES_STATE_RIBBONS_HIGH;
        break;
    case SPOTCUTIES_STATE_RIBBONS_LOW:
    case SPOTCUTIES_STATE_RIBBONS_MID:
    case SPOTCUTIES_STATE_RIBBONS_HIGH:
        TVShowConvertInternationalString(gStringVar1, show->cuties.playerName, show->cuties.language);
        TVShowConvertInternationalString(gStringVar2, show->cuties.nickname, show->cuties.pokemonNameLanguage);
        ConvertIntToDecimalString(2, show->cuties.nRibbons);
        sTVShowState = SPOTCUTIES_STATE_RIBBON_INTRO;
        break;
    case SPOTCUTIES_STATE_RIBBON_INTRO:
        TVShowConvertInternationalString(gStringVar2, show->cuties.nickname, show->cuties.pokemonNameLanguage);
        switch (show->cuties.selectedRibbon)
        {
        case CHAMPION_RIBBON:
            sTVShowState = SPOTCUTIES_STATE_RIBBON_CHAMPION;
            break;
        case COOL_RIBBON_NORMAL:
        case COOL_RIBBON_SUPER:
        case COOL_RIBBON_HYPER:
        case COOL_RIBBON_MASTER:
            sTVShowState = SPOTCUTIES_STATE_RIBBON_COOL;
            break;
        case BEAUTY_RIBBON_NORMAL:
        case BEAUTY_RIBBON_SUPER:
        case BEAUTY_RIBBON_HYPER:
        case BEAUTY_RIBBON_MASTER:
            sTVShowState = SPOTCUTIES_STATE_RIBBON_BEAUTY;
            break;
        case CUTE_RIBBON_NORMAL:
        case CUTE_RIBBON_SUPER:
        case CUTE_RIBBON_HYPER:
        case CUTE_RIBBON_MASTER:
            sTVShowState = SPOTCUTIES_STATE_RIBBON_CUTE;
            break;
        case SMART_RIBBON_NORMAL:
        case SMART_RIBBON_SUPER:
        case SMART_RIBBON_HYPER:
        case SMART_RIBBON_MASTER:
            sTVShowState = SPOTCUTIES_STATE_RIBBON_SMART;
            break;
        case TOUGH_RIBBON_NORMAL:
        case TOUGH_RIBBON_SUPER:
        case TOUGH_RIBBON_HYPER:
        case TOUGH_RIBBON_MASTER:
            sTVShowState = SPOTCUTIES_STATE_RIBBON_TOUGH;
            break;
        case WINNING_RIBBON:
            sTVShowState = SPOTCUTIES_STATE_RIBBON_WINNING;
            break;
        case VICTORY_RIBBON:
            sTVShowState = SPOTCUTIES_STATE_RIBBON_VICTORY;
            break;
        case ARTIST_RIBBON:
            sTVShowState = SPOTCUTIES_STATE_RIBBON_ARTIST;
            break;
        case EFFORT_RIBBON:
            sTVShowState = SPOTCUTIES_STATE_RIBBON_EFFORT;
            break;
        // No comment is made for any of the gift ribbons.
        // If the show is created for a gift ribbon
        // then this state will repeat indefinitely
        }
        break;
    case SPOTCUTIES_STATE_RIBBON_CHAMPION:
    case SPOTCUTIES_STATE_RIBBON_COOL:
    case SPOTCUTIES_STATE_RIBBON_BEAUTY:
    case SPOTCUTIES_STATE_RIBBON_CUTE:
    case SPOTCUTIES_STATE_RIBBON_SMART:
    case SPOTCUTIES_STATE_RIBBON_TOUGH:
    case SPOTCUTIES_STATE_RIBBON_WINNING:
    case SPOTCUTIES_STATE_RIBBON_VICTORY:
    case SPOTCUTIES_STATE_RIBBON_ARTIST:
    case SPOTCUTIES_STATE_RIBBON_EFFORT:
        TVShowConvertInternationalString(gStringVar2, show->cuties.nickname, show->cuties.pokemonNameLanguage);
        sTVShowState = SPOTCUTIES_STATE_OUTRO;
        break;
    case SPOTCUTIES_STATE_OUTRO:
        TVShowDone();
    }
    ShowFieldMessage(sTVCutiesTextGroup[state]);
}

static void DoTVShowPokemonNewsBattleFrontier(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        switch (show->frontier.facilityAndMode)
        {
        case 1:
            sTVShowState = 1;
            break;
        case 2:
            sTVShowState = 2;
            break;
        case 3:
            sTVShowState = 3;
            break;
        case 4:
            sTVShowState = 4;
            break;
        case 5:
            sTVShowState = 5;
            break;
        case 6:
            sTVShowState = 6;
            break;
        case 7:
            sTVShowState = 7;
            break;
        case 8:
            sTVShowState = 8;
            break;
        case 9:
            sTVShowState = 9;
            break;
        case 10:
            sTVShowState = 10;
            break;
        case 11:
            sTVShowState = 11;
            break;
        case 12:
            sTVShowState = 12;
            break;
        case 13:
            sTVShowState = 13;
            break;
        }
        break;
    case 1:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 14;
        break;
    case 2:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 16;
        break;
    case 3:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 15;
        break;
    case 4:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 15;
        break;
    case 5:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 14;
        break;
    case 6:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 14;
        break;
    case 7:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 14;
        break;
    case 8:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 14;
        break;
    case 9:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 14;
        break;
    case 10:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 14;
        break;
    case 11:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 14;
        break;
    case 12:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 14;
        break;
    case 13:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        ConvertIntToDecimalString(1, show->frontier.winStreak);
        sTVShowState = 14;
        break;
    case 14:
        StringCopy(gStringVar1, gSpeciesNames[show->frontier.species1]);
        StringCopy(gStringVar2, gSpeciesNames[show->frontier.species2]);
        StringCopy(gStringVar3, gSpeciesNames[show->frontier.species3]);
        sTVShowState = 18;
        break;
    case 15:
        StringCopy(gStringVar1, gSpeciesNames[show->frontier.species1]);
        StringCopy(gStringVar2, gSpeciesNames[show->frontier.species2]);
        sTVShowState = 18;
        break;
    case 16:
        StringCopy(gStringVar1, gSpeciesNames[show->frontier.species1]);
        StringCopy(gStringVar2, gSpeciesNames[show->frontier.species2]);
        StringCopy(gStringVar3, gSpeciesNames[show->frontier.species3]);
        sTVShowState = 17;
        break;
    case 17:
        StringCopy(gStringVar1, gSpeciesNames[show->frontier.species4]);
        sTVShowState = 18;
        break;
    case 18:
        TVShowConvertInternationalString(gStringVar1, show->frontier.playerName, show->frontier.language);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVPokemonNewsBattleFrontierTextGroup[state]);
}

static void DoTVShowWhatsNo1InHoennToday(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        TVShowConvertInternationalString(gStringVar1, show->numberOne.playerName, show->numberOne.language);
        switch (show->numberOne.actionIdx)
        {
        case 0:
            sTVShowState = 1;
            break;
        case 1:
            sTVShowState = 2;
            break;
        case 2:
            sTVShowState = 3;
            break;
        case 3:
            sTVShowState = 4;
            break;
        case 4:
            sTVShowState = 5;
            break;
        case 5:
            sTVShowState = 6;
            break;
        case 6:
            sTVShowState = 7;
            break;
        }
        break;
    case 1:
        TVShowConvertInternationalString(gStringVar1, show->numberOne.playerName, show->numberOne.language);
        ConvertIntToDecimalString(1, show->numberOne.count);
        sTVShowState = 8;
        break;
    case 2:
        TVShowConvertInternationalString(gStringVar1, show->numberOne.playerName, show->numberOne.language);
        ConvertIntToDecimalString(1, show->numberOne.count);
        sTVShowState = 8;
        break;
    case 3:
        TVShowConvertInternationalString(gStringVar1, show->numberOne.playerName, show->numberOne.language);
        ConvertIntToDecimalString(1, show->numberOne.count);
        sTVShowState = 8;
        break;
    case 4:
        TVShowConvertInternationalString(gStringVar1, show->numberOne.playerName, show->numberOne.language);
        ConvertIntToDecimalString(1, show->numberOne.count);
        sTVShowState = 8;
        break;
    case 5:
        TVShowConvertInternationalString(gStringVar1, show->numberOne.playerName, show->numberOne.language);
        ConvertIntToDecimalString(1, show->numberOne.count);
        sTVShowState = 8;
        break;
    case 6:
        TVShowConvertInternationalString(gStringVar1, show->numberOne.playerName, show->numberOne.language);
        ConvertIntToDecimalString(1, show->numberOne.count);
        sTVShowState = 8;
        break;
    case 7:
        TVShowConvertInternationalString(gStringVar1, show->numberOne.playerName, show->numberOne.language);
        ConvertIntToDecimalString(1, show->numberOne.count);
        sTVShowState = 8;
        break;
    case 8:
        TVShowConvertInternationalString(gStringVar1, show->numberOne.playerName, show->numberOne.language);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVWhatsNo1InHoennTodayTextGroup[state]);
}

u8 SecretBaseSecrets_GetNumActionsTaken(TVShow *show)
{
    u8 i;
    u8 flagsSet;

    for (i = 0, flagsSet = 0; i < NUM_SECRET_BASE_FLAGS; i++)
    {
        if ((show->secretBaseSecrets.flags >> i) & 1)
            flagsSet++;
    }
    return flagsSet;
}

static u8 SecretBaseSecrets_GetStateByFlagNumber(TVShow *show, u8 flagId)
{
    u8 i;
    u8 flagsSet;

    for (i = 0, flagsSet = 0; i < NUM_SECRET_BASE_FLAGS; i++)
    {
        if ((show->secretBaseSecrets.flags >> i) & 1)
        {
            if (flagsSet == flagId)
                return sTVSecretBaseSecretsActions[i];

            flagsSet++;
        }
    }
    return 0;
}

static void DoTVShowSecretBaseSecrets(void)
{
    TVShow *show;
    u8 state;
    u8 numActions;
    u16 i;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case SBSECRETS_STATE_INTRO:
        TVShowConvertInternationalString(gStringVar1, show->secretBaseSecrets.baseOwnersName, show->secretBaseSecrets.baseOwnersNameLanguage);
        TVShowConvertInternationalString(gStringVar2, show->secretBaseSecrets.playerName, show->secretBaseSecrets.language);
        numActions = SecretBaseSecrets_GetNumActionsTaken(show);
        if (numActions == 0)
        {
            sTVShowState = SBSECRETS_STATE_NOTHING_USED1;
        }
        else
        {
            show->secretBaseSecrets.savedState = SBSECRETS_STATE_DO_NEXT1;
            sTVSecretBaseSecretsRandomValues[0] = Random() % numActions;
            sTVShowState = SecretBaseSecrets_GetStateByFlagNumber(show, sTVSecretBaseSecretsRandomValues[0]);
        }
        break;
    case SBSECRETS_STATE_DO_NEXT1:
        TVShowConvertInternationalString(gStringVar2, show->secretBaseSecrets.playerName, show->secretBaseSecrets.language);
        numActions = SecretBaseSecrets_GetNumActionsTaken(show);
        switch (numActions)
        {
        case 1:
            sTVShowState = SBSECRETS_STATE_NOTHING_USED2;
            break;
        case 2:
            show->secretBaseSecrets.savedState = SBSECRETS_STATE_DO_NEXT2;
            if (sTVSecretBaseSecretsRandomValues[0] == 0)
                sTVShowState = SecretBaseSecrets_GetStateByFlagNumber(show, 1);
            else
                sTVShowState = SecretBaseSecrets_GetStateByFlagNumber(show, 0);
            break;
        default:
            for (i = 0; i < 0xFFFF; i++)
            {
                sTVSecretBaseSecretsRandomValues[1] = Random() % numActions;
                if (sTVSecretBaseSecretsRandomValues[1] != sTVSecretBaseSecretsRandomValues[0])
                    break;
            }
            show->secretBaseSecrets.savedState = SBSECRETS_STATE_DO_NEXT2;
            sTVShowState = SecretBaseSecrets_GetStateByFlagNumber(show, sTVSecretBaseSecretsRandomValues[1]);
            break;
        }
        break;
    case SBSECRETS_STATE_DO_NEXT2:
        TVShowConvertInternationalString(gStringVar2, show->secretBaseSecrets.playerName, show->secretBaseSecrets.language);
        numActions = SecretBaseSecrets_GetNumActionsTaken(show);
        if (numActions == 2)
        {
            sTVShowState = SBSECRETS_STATE_NOTHING_USED2;
        }
        else
        {
            for (i = 0; i < 0xFFFF; i++)
            {
                sTVSecretBaseSecretsRandomValues[2] = Random() % numActions;
                if (sTVSecretBaseSecretsRandomValues[2] != sTVSecretBaseSecretsRandomValues[0] && sTVSecretBaseSecretsRandomValues[2] != sTVSecretBaseSecretsRandomValues[1])
                    break;
            }
            show->secretBaseSecrets.savedState = SBSECRETS_STATE_TOOK_X_STEPS;
            sTVShowState = SecretBaseSecrets_GetStateByFlagNumber(show, sTVSecretBaseSecretsRandomValues[2]);
        }
        break;
    case SBSECRETS_STATE_TOOK_X_STEPS:
        TVShowConvertInternationalString(gStringVar1, show->secretBaseSecrets.baseOwnersName, show->secretBaseSecrets.baseOwnersNameLanguage);
        TVShowConvertInternationalString(gStringVar2, show->secretBaseSecrets.playerName, show->secretBaseSecrets.language);
        ConvertIntToDecimalString(2, show->secretBaseSecrets.stepsInBase);
        if (show->secretBaseSecrets.stepsInBase <= 30)
            sTVShowState = SBSECRETS_STATE_BASE_INTEREST_LOW;
        else if (show->secretBaseSecrets.stepsInBase <= 100)
            sTVShowState = SBSECRETS_STATE_BASE_INTEREST_MED;
        else
            sTVShowState = SBSECRETS_STATE_BASE_INTEREST_HIGH;
        break;
    case SBSECRETS_STATE_BASE_INTEREST_LOW ... SBSECRETS_STATE_BASE_INTEREST_HIGH:
        TVShowConvertInternationalString(gStringVar1, show->secretBaseSecrets.baseOwnersName, show->secretBaseSecrets.baseOwnersNameLanguage);
        TVShowConvertInternationalString(gStringVar2, show->secretBaseSecrets.playerName, show->secretBaseSecrets.language);
        sTVShowState = SBSECRETS_STATE_OUTRO;
        break;
    case SBSECRETS_STATE_OUTRO:
        TVShowConvertInternationalString(gStringVar1, show->secretBaseSecrets.baseOwnersName, show->secretBaseSecrets.baseOwnersNameLanguage);
        TVShowConvertInternationalString(gStringVar2, show->secretBaseSecrets.playerName, show->secretBaseSecrets.language);
        TVShowDone();
        break;
    // All below states are descriptions of what the player interacted with while in the secret base
    case SBSECRETS_STATE_NOTHING_USED1:
        sTVShowState = SBSECRETS_STATE_TOOK_X_STEPS;
        break;
    case SBSECRETS_STATE_NOTHING_USED2:
        sTVShowState = SBSECRETS_STATE_TOOK_X_STEPS;
        break;
    case SBSECRETS_STATE_USED_CHAIR ... SBSECRETS_STATE_USED_MUD_BALL:
        sTVShowState = show->secretBaseSecrets.savedState;
        break;
    case SBSECRETS_STATE_USED_BAG:
        StringCopy(gStringVar2, ItemId_GetName(show->secretBaseSecrets.item));
        sTVShowState = show->secretBaseSecrets.savedState;
        break;
    case SBSECRETS_STATE_USED_CUSHION:
        // Randomly decide based on trainer ID if the player hugged or hit the cushion
        if (show->common.trainerIdLo & 1)
            sTVShowState = SBSECRETS_STATE_HUGGED_CUSHION;
        else
            sTVShowState = SBSECRETS_STATE_HIT_CUSHION;
        break;
    case SBSECRETS_STATE_HIT_CUSHION ... SBSECRETS_NUM_STATES:
        sTVShowState = show->secretBaseSecrets.savedState;
        break;
    }
    ShowFieldMessage(sTVSecretBaseSecretsTextGroup[state]);
}

static void DoTVShowSafariFanClub(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case 0:
        if (show->safariFanClub.monsCaught == 0)
            sTVShowState = 6;
        else if (show->safariFanClub.monsCaught < 4)
            sTVShowState = 5;
        else
            sTVShowState = 1;
        break;
    case 1:
        TVShowConvertInternationalString(gStringVar1, show->safariFanClub.playerName, show->safariFanClub.language);
        ConvertIntToDecimalString(1, show->safariFanClub.monsCaught);
        if (show->safariFanClub.pokeblocksUsed == 0)
            sTVShowState = 3;
        else
            sTVShowState = 2;
        break;
    case 2:
        ConvertIntToDecimalString(1, show->safariFanClub.pokeblocksUsed);
        sTVShowState = 4;
        break;
    case 3:
        sTVShowState = 4;
        break;
    case 4:
        TVShowConvertInternationalString(gStringVar1, show->safariFanClub.playerName, show->safariFanClub.language);
        sTVShowState = 10;
        break;
    case 5:
        TVShowConvertInternationalString(gStringVar1, show->safariFanClub.playerName, show->safariFanClub.language);
        ConvertIntToDecimalString(1, show->safariFanClub.monsCaught);
        if (show->safariFanClub.pokeblocksUsed == 0)
            sTVShowState = 8;
        else
            sTVShowState = 7;
        break;
    case 6:
        TVShowConvertInternationalString(gStringVar1, show->safariFanClub.playerName, show->safariFanClub.language);
        if (show->safariFanClub.pokeblocksUsed == 0)
            sTVShowState = 8;
        else
            sTVShowState = 7;
        break;
    case 7:
        ConvertIntToDecimalString(1, show->safariFanClub.pokeblocksUsed);
        sTVShowState = 9;
        break;
    case 8:
        sTVShowState = 9;
        break;
    case 9:
        TVShowConvertInternationalString(gStringVar1, show->safariFanClub.playerName, show->safariFanClub.language);
        sTVShowState = 10;
        break;
    case 10:
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVSafariFanClubTextGroup[state]);
}

// This show is a version of Contest Live Updates for the Lilycove Contest Lady
static void DoTVShowLilycoveContestLady(void)
{
    TVShow *show;
    u8 state;

    show = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004];
    gSpecialVar_Result = FALSE;
    state = sTVShowState;
    switch (state)
    {
    case CONTESTLADYLIVE_STATE_INTRO:
        BufferContestName(gStringVar1, show->contestLady.contestCategory);
        if (show->contestLady.pokeblockState == CONTEST_LADY_GOOD)
            sTVShowState = CONTESTLADYLIVE_STATE_WON;
        else if (show->contestLady.pokeblockState == CONTEST_LADY_NORMAL)
            sTVShowState = CONTESTLADYLIVE_STATE_LOST;
        else // CONTEST_LADY_BAD
            sTVShowState = CONTESTLADYLIVE_STATE_LOST_BADLY;
        break;
    case CONTESTLADYLIVE_STATE_WON:
    case CONTESTLADYLIVE_STATE_LOST:
        TVShowConvertInternationalString(gStringVar3, show->contestLady.playerName, show->contestLady.language);
    case CONTESTLADYLIVE_STATE_LOST_BADLY:
        TVShowConvertInternationalString(gStringVar2, show->contestLady.nickname, show->contestLady.pokemonNameLanguage);
        TVShowDone();
        break;
    }
    ShowFieldMessage(sTVLilycoveContestLadyTextGroup[state]);
}

static void TVShowDone(void)
{
    gSpecialVar_Result = TRUE;
    sTVShowState = 0;
    gSaveBlock1Ptr->tvShows[gSpecialVar_0x8004].common.active = FALSE;
}

void ResetTVShowState(void)
{
    sTVShowState = 0;
}
