Skip to content

Better White Out Money Calculation

FieryMewtwo edited this page Nov 1, 2021 · 8 revisions

Credits to AsparagusEduardo for creating the implementation of this feature that I will be explaining here, and to lightgod87 for extra polishing it.

In Pokémon Emerald, whenever you lose a battle you always lose half of your money.

In Pokémon FireRed/LeafGreen, Diamond/Pearl and subsequent games, you lose a more sensible amount influenced by the Pokémon with the highest level of your party and the amount of badges you have.

Today we'll be implementing that in Pokeemerald.

1. Adding in some new battle strings

This implementation introduces 2 text strings from FireRed/LeafGreen for extra flavor and faithfulness. This is super easy, so let's get it out of the way.

First we define them by adding a constant in include/constants/battle_string_ids.h:

+#define STRINGID_PLAYERLOSTTOENEMYTRAINER   381
+#define STRINGID_PLAYERPAIDPRIZEMONEY       382

Don't forget to add 2 to the current value of your BATTLESTRINGS_COUNT in the same file!

And then we add the actual text strings to src/battle_message.c.

As to not complicate things, I suggest to put them right below sText_YouThrowABallNowRight, but you can add them wherever you want.

+static const u8 sText_PlayerLostToEnemyTrainer[] = _("{B_PLAYER_NAME} is out of\nusable POKéMON!\pPlayer lost against\n{B_TRAINER1_CLASS} {B_TRAINER1_NAME}!{PAUSE_UNTIL_PRESS}");
+static const u8 sText_PlayerPaidPrizeMoney[] = _("{B_PLAYER_NAME} paid ¥{B_BUFF1} as the prize\nmoney…\p… … … …\p{B_PLAYER_NAME} whited out!{PAUSE_UNTIL_PRESS}");

Now we have to link the constants we added previously to the labels of these new text strings by registering them in the gBattleStringsTable.

+    [STRINGID_PLAYERLOSTTOENEMYTRAINER - 12] = sText_PlayerLostToEnemyTrainer,
+    [STRINGID_PLAYERPAIDPRIZEMONEY - 12] = sText_PlayerPaidPrizeMoney,

Again, you can add these wherever you want. If you want to remain consistent, you would want once again add them right under sText_YouThrowABallNowRight, but it doesn't matter as long as you add them to the table.

2. Modifying the White Out BattleScript

Let's put these new battle strings to use, shall we? To do that, we'll do some quick changes to the BattleScript that triggers when you lose a battle, i.e. BattleScript_LocalBattleLostPrintWhiteOut in data/battle_scrpts_1.s.

BattleScript_LocalBattleLostPrintWhiteOut::
+	jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_LocalBattleLostEnd
	printstring STRINGID_PLAYERWHITEOUT
	waitmessage B_WAIT_TIME_LONG
+	getmoneyreward
	printstring STRINGID_PLAYERWHITEOUT2
	waitmessage B_WAIT_TIME_LONG
+	end2
BattleScript_LocalBattleLostEnd::
+	printstring STRINGID_PLAYERLOSTTOENEMYTRAINER
+	waitmessage B_WAIT_TIME_LONG
+	getmoneyreward
+	printstring STRINGID_PLAYERPAIDPRIZEMONEY
+	waitmessage B_WAIT_TIME_LONG
	end2

Now we'll tweak the text string linked to the constant STRINGID_PLAYERWHITEOUT2 in order to inform the Player about the amount of money they're losing, which is sText_PlayerWhiteout2 located in src/battle_message.c.

-static const u8 sText_PlayerWhiteout2[] = _("{B_PLAYER_NAME} whited out!{PAUSE_UNTIL_PRESS}");
+static const u8 sText_PlayerWhiteout2[] = _("{B_PLAYER_NAME} panicked and lost ¥{B_BUFF1}…\p… … … …\p{B_PLAYER_NAME} whited out!{PAUSE_UNTIL_PRESS}");

So, what are we doing here? Here we're telling the game to run the BattleScript_LocalBattleLostEnd if the Player has lost a trainer battle, in order to make use of the new text strings we inserted earlier. If the current battle type is not a trainerbattle though (which outside of special battle facilities means we're losing a wildbattle), then the game will run BattleScript_LocalBattleLostPrintWhiteOut normally.

Lastly, we're setting things up so the Cmd_getmoneyreward function will be triggered when the Player loses a battle.

Now, you may be wondering; why are we doing that? Normally, Cmd_getmoneyreward is in charge of calculating the money you receive as a reward for winning and that alone after all.

You can guess what are we going to change next, can't you?

3. Modifying the Cmd_getmoneyreward function

Now we want to open src/battle_script_commands.c. Before anything, we have to define 2 array lists that we're going to use soon enough.

+static const u16 sBadgeFlags[8] = {
+    FLAG_BADGE01_GET, FLAG_BADGE02_GET, FLAG_BADGE03_GET, FLAG_BADGE04_GET,
+    FLAG_BADGE05_GET, FLAG_BADGE06_GET, FLAG_BADGE07_GET, FLAG_BADGE08_GET,
+};

+static const u16 sWhiteOutBadgeMoney[9] = { 8, 16, 24, 36, 48, 60, 80, 100, 120 };

Note: The base payout while the Player holds 5 badges has been bumped from 60 to 64 starting from Pokémon Black and White, so you may want to do the same here, or not. It's entirely up to you.

If I were you I'd add them right below sBattlePalaceNatureToFlavorTextId, that is right before the first function in the file.

And now we're ready to actually modify Cmd_getmoneyreward:

static void Cmd_getmoneyreward(void)
{
-    u32 moneyReward = GetTrainerMoneyToGive(gTrainerBattleOpponent_A);
-    if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
-        moneyReward += GetTrainerMoneyToGive(gTrainerBattleOpponent_B);
+    u32 money;
+    u8 sPartyLevel = 1;

-    AddMoney(&gSaveBlock1Ptr->money, moneyReward);
-    PREPARE_WORD_NUMBER_BUFFER(gBattleTextBuff1, 5, moneyReward);
+    if (gBattleOutcome == B_OUTCOME_WON)
+    {
+        money = GetTrainerMoneyToGive(gTrainerBattleOpponent_A);
+        if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
+            money += GetTrainerMoneyToGive(gTrainerBattleOpponent_B);
+        AddMoney(&gSaveBlock1Ptr->money, money);
+    }
+    else
+    {
+        s32 i, count;
+        for (i = 0; i < PARTY_SIZE; i++)
+        {
+            if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2) != SPECIES_NONE
+             && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2) != SPECIES_EGG)
+            {
+                if (GetMonData(&gPlayerParty[i], MON_DATA_LEVEL) > sPartyLevel)
+                    sPartyLevel = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);
+            }
+        }
+        for (count = 0, i = 0; i < ARRAY_COUNT(sBadgeFlags); i++)
+        {
+            if (FlagGet(sBadgeFlags[i]) == TRUE)
+                ++count;
+        }
+        money = sWhiteOutBadgeMoney[count] * sPartyLevel;
+        RemoveMoney(&gSaveBlock1Ptr->money, money);
+    }

+   PREPARE_WORD_NUMBER_BUFFER(gBattleTextBuff1, 5, money);
    gBattlescriptCurrInstr++;
}

Here, we're making Cmd_getmoneyreward handle not only the money calculation for winning a battle, but we're also making it calculate how much money should be removed from the Player when they lose a battle, and then we're storing that amount in a buffer to display it in the STRINGID_PLAYERPAIDPRIZEMONEY we added earlier.

4. Modifying the DoWhiteOut function

This function which can be found in the src/overworld.c file is always triggered whenever you lose a battle and it's also in charge of cutting the Player's money by half whenever they lose a battle, as you can easily tell by reading it.

As the last step of this process, we'll remove that bit because we naturally don't want that to happen.

void DoWhiteOut(void)
{
    ScriptContext2_RunNewScript(EventScript_WhiteOut);
-   SetMoney(&gSaveBlock1Ptr->money, GetMoney(&gSaveBlock1Ptr->money) / 2);
    HealPlayerParty();
    Overworld_ResetStateAfterWhiteOut();
    SetWarpDestinationToLastHealLocation();
    WarpIntoMap();
}

And just like that, we're done. Go build a ROM and let a Magikarp faint your party or something.

Clone this wiki locally