Simple Modifications Directory Page 5

Started by Hiroshi Sotomura December 30th, 2018 2:48 PM
  • 223301 views
  • 464 replies
Male
Seen 14 Hours Ago
Posted October 27th, 2022
238 posts
7.2 Years
[Pokeemerald] Toggle Trainers "Seeing" You

This is probably mostly useful for a debug menu, but could have utility for certain game events.
  • Add FLAG_TOGGLE_TRAINER_BATTLES in include/constants/flags
  • Change CheckForTrainersWantingBattle in src/trainer_see.c to:
    bool8 CheckForTrainersWantingBattle(void)
    {
        u8 i;
        
        if (FlagGet(FLAG_TOGGLE_TRAINER_BATTLES))
            return FALSE;
        
        gNoOfApproachingTrainers = 0;
        //etc...
  • set FLAG_TOGGLE_TRAINER_BATTLES in a script or function!

Obviously, still talking directly to the trainer will trigger the battle, but they won't catch sight of you.
Male
Seen 55 Minutes Ago
Posted 15 Hours Ago
75 posts
7.1 Years
[EM] FRLG / Gen IV and Onwards White Out Money Calculation (with messages)

This implementation is based on AsparagusEduardo's implementation of that same feature, but I added the required messages, taking the FRLG decomp as a point of start, and then modifying the getmoneyreward function in battle_script_commands.c, so it handles this part better and the buffer is updated, so the money value is taken correctly.



You can pull this feature from this branch:

GitHub Link:
https://github.com/lightgod87/pokeemerald/tree/whiteoutmoney

Thanks to GriffinR and AsparagusEduardo for making this possible.
There's something VERY wrong about the code, but I'm not 100% sure. Yesterday I installed (manually) 4 simple modifications of these posts, one of them being this one. For some reason, after a won battle with any trainer, the game would crash. Manually reverting the code did nothing. In fact, a backup of my repository and the same repository with the 4 modifications reverted (so theoretically the same, right? I may be wrong?) behaved differently: one worked, one crashed.

Anyways, I reverted to my "clean" copy and everything worked. Tried installing all 4 modifications again and this time the battles didn't crash but instead, the next battle after a double battle, the first Pokémon sent by the trainer was a ?. who instantly froze the game. This bug worked in any double battle and any trainer afterwards. Again, reverting the code did nothing. In fact, I tried copying over only the affected files from my backup. Didn't work, the game remained broken.

It wasn't me, since the day before I literally beat the game as a test with the version I had a backup of and never had a single problem like this. After a few tries I installed all 3 modifications except this one, and it seems to work perfectly. Battles end fine and battles after double battles work. None of these modifications affect battles, so it makes sense.

After reading the code (keeping in mind that I'm still learning how to read and write code) I can't find anything that can make this happen, except this line added in src/battle_main.c:
EWRAM_DATA u8 gMaxPartyLevel = 1;
I have literally no idea how EWRAM works in this game, so I may be pulling this from my ass. But it's the only thing that maybe persists after compiling? I dunno, I think the most interesting fact is that it remains after reverting the code and compiling again.

Please excuse me if this makes sense and it's normal that code persists even after reverting it, since again I'm still learning to code. Correct me if I'm wrong.
Seen March 22nd, 2021
Posted July 1st, 2020
4 posts
5.1 Years
Hello, not sure if this the right place to ask. I am trying to add a new species with help of the wiki tutorial. I did everything up to step 2, then tried to compile the game but I get this error when doing so.

"tools/gbagfx/gbagfx.exe graphics/pokemon/firedog/anim_front.png graphics/pokemon/firedog/anim_front.4bpp
"graphics/pokemon/firedog/anim_front.png" has an unsupported color type.
make: *** [Makefile:209: graphics/pokemon/firedog/anim_front.4bpp]"

Anyone knows what the problem could be? Thanks in advance!

Lunos

Random Uruguayan User

Male
Montevideo (Uruguay)
Seen 10 Hours Ago
Posted 1 Day Ago
3,006 posts
14.7 Years
Hello, not sure if this the right place to ask. I am trying to add a new species with help of the wiki tutorial. I did everything up to step 2, then tried to compile the game but I get this error when doing so.

"tools/gbagfx/gbagfx.exe graphics/pokemon/firedog/anim_front.png graphics/pokemon/firedog/anim_front.4bpp
"graphics/pokemon/firedog/anim_front.png" has an unsupported color type.
make: *** [Makefile:209: graphics/pokemon/firedog/anim_front.4bpp]"

Anyone knows what the problem could be? Thanks in advance!
You may have indexed Firedog's anim_front.png incorrectly.
Make sure the sprite has a maximum of 16 colors, and that the first color in its palette is the same color used for the background.
Seen March 22nd, 2021
Posted July 1st, 2020
4 posts
5.1 Years
You may have indexed Firedog's anim_front.png incorrectly.
Make sure the sprite has a maximum of 16 colors, and that the first color in its palette is the same color used for the background.
I got anim_front.png and back.png to work but now footprint.png have the same problem as before even the index 0 is white because the background is white and the footprint is black.




I got the footprint to work! But now there's an issue with the normal.pal I think, here's my RGB values for the sprite:

"JASC-PAL
0100
16
239 229 177
81 145 209
17 17 17
73 73 81
169 169 169
249 249 249
201 121 169
255 0 255
255 0 255
255 0 255
255 0 255
255 0 255
255 0 255
255 0 255
255 0 255
16 16 16 "

And I got this error:

"tools/gbagfx/gbagfx.exe graphics/pokemon/firedog/normal.pal graphics/pokemon/firedog/normal.gbapal
Garbage after blue color component.
make: *** [Makefile:211: graphics/pokemon/firedog/normal.gbapal] "

I am not sure what line correlates to what.
Male
Seen 14 Hours Ago
Posted October 27th, 2022
238 posts
7.2 Years
[Pokeemerald] Plural giveitem

Currently the game only properly converts giveitem to a plural string if you are receiving a berry or a poke ball. So I fixed it for all items.



Here is the repo to pull from. Alternatively, follow the wiki guide

How to pull from the repo:
Spoiler:
Male
Seen 14 Hours Ago
Posted October 27th, 2022
238 posts
7.2 Years
[Pokeemerald] Run Custom Scripts on Trainer Sight

By default trainers will only function properly if the first argument is a trainerbattle command. This feature allows you to run any script instead:



Here is the repo. You can pull from it or follow the wiki post I made. This has set up instructions at the bottom.

Buffel Saft

Male
Seen 5 Hours Ago
Posted 3 Weeks Ago
1,550 posts
9.7 Years
LGPE-style Premier Balls (Emerald)

In Let's Go Pikachu and Eevee, buying Poké Balls in bulk will give the player one Premier Ball for every ten balls purchased (e.g. buying 30 balls at once gives you three free Premier Balls). This also applies to all types of ball, instead of just regular Poké Balls.

To implement this in pokeemerald, go to src/shop.c and change Task_ReturnToItemListAfterItemPurchase to the following:
Spoiler:
static void Task_ReturnToItemListAfterItemPurchase(u8 taskId)
{
    s16 *data = gTasks[taskId].data;

    if (gMain.newKeys & (A_BUTTON | B_BUTTON))
    {
        PlaySE(SE_SELECT);
        if ((ItemId_GetPocket(tItemId) == POCKET_POKE_BALLS) && tItemCount > 9 && AddBagItem(ITEM_PREMIER_BALL, tItemCount / 10) == TRUE)
        {
            if (tItemCount > 19)
            {
                BuyMenuDisplayMessage(taskId, gText_ThrowInPremierBalls, BuyMenuReturnToItemList);
            }
            else
            {
                BuyMenuDisplayMessage(taskId, gText_ThrowInPremierBall, BuyMenuReturnToItemList);
            }
        }
        else if((ItemId_GetPocket(tItemId) == POCKET_TM_HM))
        {
            RedrawListMenu(tListTaskId);
            BuyMenuReturnToItemList(taskId);
        }
        else
        {
            BuyMenuReturnToItemList(taskId);
        }
    }
}

Then, add an extra string to src/strings.c, like so:
const u8 gText_ThrowInPremierBalls[] = _("I'll throw in some Premier Balls, too.{PAUSE_UNTIL_PRESS}");
And don't forget to declare it in include/strings.h:
extern const u8 gText_ThrowInPremierBalls[];

Buffel Saft

Male
Seen 5 Hours Ago
Posted 3 Weeks Ago
1,550 posts
9.7 Years
Change Time-Based Evolution Times (Emerald)
For whatever reason, Game Freak decided that day time in Gen III should be from 12:00 p.m. to 11:59 p.m. (and night time should be 12:00 a.m. to 11:59 a.m.). As there's no day/night cycle, it only really affected Eevee's evolution into Espeon or Umbreon. To change these to more sensible values, first open up include/pokemon.h and define two new constants:
#define DAY_START 4
#define NIGHT_START 18
These are in 24 hour time, and will determine when day and night start. Set them to any value you want (as long as it's between 0 and 24). Then, go to src/pokemon.c and make the following text replacements:
  • Replace gLocalTime.hours >= 12 && gLocalTime.hours < 24 with gLocalTime.hours >= DAY_START && gLocalTime.hours < NIGHT_START
  • Replace gLocalTime.hours >= 0 && gLocalTime.hours < 12 with (gLocalTime.hours >= NIGHT_START || gLocalTime.hours < DAY_START)
If you're using Dizzy Egg's repos you may have a few instances of these, but otherwise there's only one of each. Also, if you've chosen different day/night start times, double check that the AND and OR conditions above still make sense.
e.g. if you have something like DAY_START = 12 and NIGHT_START = 0 (so you'd have gLocalTime.hours >= 12 && gLocalTime.hours < 0), that won't work (because the gLocalTime.hours can't be greater than 12 and less than 0 at the same time).
Seen November 1st, 2020
Posted October 28th, 2020
21 posts
6 Years
Shops with single items to buy [EM]
In later Generations, there are some shops that would only let you buy one of each item (like TM Shops), so I created a pokeemerald branch that implements the option to create shops of this type.

How to use:
  1. You first need to clone this branch into your project.
  2. Whenever you call the pokemart command in your map scripts, you can add a number that represents a TM Shop's id. Since 0 is the default value, setting it to 0 won't work. If you repeat that shop's id in another one, they will share the same flags.
    Spoiler:

    @before
    pokemart MauvilleCity_Mart_Pokemart
    @after
    pokemart MauvilleCity_Mart_Pokemart, 1

  3. You can specify the amount of TMShops that you want to implement in include\shop.h.
    Spoiler:

    #define TMSHOP_COUNT 4
    #define TMSHOP_ITEMS_COUNT 16

  4. By default, the max amount of items per TM Shop is 16, so any item added past this will be ignored and not added to the list.
  5. If for any reason you need more than 16 items per TM Shop, you'll need to change the data type from u16 to u32 or u64 in the following sections:
    Spoiler:
    • In src\shop.c
      static void SetTMShopItemsForSale(const u16 *items)
      {
          u16 i = 0; //<-- Change this one
      
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      
      bool8 GetSetItemBought(u8 storeId, u16 itemPos, u8 caseId)
      {
          u16 mask; //<--Change this one
    • In include\global.h
      u16 tmShopFlags[TMSHOP_COUNT];

Thanks to:
  • UltimaSoul for their help debugging and teaching me how to bit shift :P
  • ghoulslash for advice and the inspiration for this branch
This is a fantastic feature, I really like it! I only have one question: the number of TM shops (TMSHOP_COUNT) is defined as 4. Does this mean that there can be a maximum of four individual TM shops? If so, can the number of TM shops be increased or will it cause adverse effects (seeing as we are also modifying the size of the save block)?

Thank you for your modifications, they are awesome!
Age 29
Male
Seen April 13th, 2023
Posted December 26th, 2022
27 posts
9.3 Years
This is a fantastic feature, I really like it! I only have one question: the number of TM shops (TMSHOP_COUNT) is defined as 4. Does this mean that there can be a maximum of four individual TM shops? If so, can the number of TM shops be increased or will it cause adverse effects (seeing as we are also modifying the size of the save block)?

Thank you for your modifications, they are awesome!
Yes, 4 is the default maximum. I left it like that for testing, but as far as I know if you have enough space in the save block for more, there shouldn't be an issue increasing it.

Lunos

Random Uruguayan User

Male
Montevideo (Uruguay)
Seen 10 Hours Ago
Posted 1 Day Ago
3,006 posts
14.7 Years
Let the Player only buy TMs and HMs once (Emerald)

I think the title is self-explanatory, isn't it?
The code changes presented here will make it so you can only buy TMs or HMs out of a PokéMart once and never again.
This is intended to complement the Reusable TMs feature posted by Paccy in this same thread.

Quick Showcase:


Link:
https://github.com/LOuroboros/pokeemerald/commit/2e6af4e1c4a55e100882d0ccb009d91cef8c0259

To implement the feature on a project, the code can be implemented manually.
Alternatively, you can track my Pokeemerald Repository via
git remote
and then pull from the branch in which the commit is located, assuming your project is up-to-date with upstream (Pret's repository).

Ex:
git remote add lunos https://github.com/LOuroboros/pokeemerald
git pull lunos OneTimePurchase_TMsHMs
Bugs:
-For a single frame right after purchasing a TM, the buy menu overlaps the text box, reported by AsparagusEduardo on Discord
Fixed.

And that's pretty much it.



EDIT:
Gen VIII style Premier Balls (Emerald)
There's a very, very small issue in your modification, it's definitely not super important but I think it's worth mentioning.
The msgbox shouldn't be flashing before the "Here you go!" message shows up.

Vanilla:


Your
Task_ReturnToItemListAfterItemPurchase
:


This isn't really a Gen. 8 feature, btw. It started with LGPE which is widely considered a Gen. 7 game.

Buffel Saft

Male
Seen 5 Hours Ago
Posted 3 Weeks Ago
1,550 posts
9.7 Years

There's a very, very small issue in your modification, it's definitely not super important but I think it's worth mentioning.
The msgbox shouldn't be flashing before the "Here you go!" message shows up.

Vanilla:


Your
Task_ReturnToItemListAfterItemPurchase
:


This isn't really a Gen. 8 feature, btw. It started with LGPE which is widely considered a Gen. 7 game.
Oh, I haven't played LGPE and didn't know that, will update the post. I'll look into the flashing issue too, thanks for pointing that out!

Lunos

Random Uruguayan User

Male
Montevideo (Uruguay)
Seen 10 Hours Ago
Posted 1 Day Ago
3,006 posts
14.7 Years
Let a Pokémon forget any move they know (Emerald)

As you all know, there's certain moves that a Pokémon can't forget on their own, these are the Hidden Machine Moves.
There is a function which handles that, it's called
CanReplaceMove
and it can be found in the
src/pokemon_summary_screen.c
file.
By making this function always return a value of
TRUE
, we can let any Pokémon forget any of their moves at will.

To do this, let's open up the
src/pokemon_summary_screen.c
file and change the
CanReplaceMove
function from this:
static bool8 CanReplaceMove(void)
{
    if (sMonSummaryScreen->firstMoveIndex == MAX_MON_MOVES
        || sMonSummaryScreen->newMove == MOVE_NONE
        || IsMoveHm(sMonSummaryScreen->summary.moves[sMonSummaryScreen->firstMoveIndex]) != TRUE)
        return TRUE;
    else
        return FALSE;
}
To this:
static bool8 CanReplaceMove(void)
{
    return TRUE;
}
Save, build a ROM and that's basically it.





Alternatively, we can instead remove this function and adjust the code where it's called from accordingly.
Thankfully, the
CanReplaceMove
function is only called in the
Task_HandleReplaceMoveInput
function, so basically we'd need to:
1) Remove the declaration of the
CanReplaceMove
function (Line 214 by default).
2) Remove the
CanReplaceMove
function (Lines 2210 to 2218 by default).
3) Adjust the
if
check of the
Task_HandleReplaceMoveInput
function where
CanReplaceMove
is called, changing it from this:
            else if (gMain.newKeys & A_BUTTON)
            {
                if (CanReplaceMove() == TRUE)
                {
                    StopPokemonAnimations();
                    PlaySE(SE_SELECT);
                    sMoveSlotToReplace = sMonSummaryScreen->firstMoveIndex;
                    gSpecialVar_0x8005 = sMoveSlotToReplace;
                    BeginCloseSummaryScreen(taskId);
                }
                else
                {
                    PlaySE(SE_HAZURE);
                    ShowCantForgetHMsWindow(taskId);
                }
            }
To this:
            else if (gMain.newKeys & A_BUTTON)
            {
                StopPokemonAnimations();
                PlaySE(SE_SELECT);
                sMoveSlotToReplace = sMonSummaryScreen->firstMoveIndex;
                gSpecialVar_0x8005 = sMoveSlotToReplace;
                BeginCloseSummaryScreen(taskId);
            }
Quick Showcase:


And that's pretty much it.





EDIT: I completely forgot to link it here, but thespbgamer brought up a fair point in this thread about 3 months after I originally posted this.
The changes I present in this post don't cover the replacement of a move in battle, because I absolutely forgot that was a thing honestly.
Click here if you want to learn how to address that.

EDIT2: And just as I finish editing this post, Jaizu is telling me that I also forgot to mention the removal of a move after evolution.
Just go to
src/evolution_scene.c
and remove the 2
if
statements that call the
IsHMMove2
function.
That means you have to do something like this:
Spoiler:

As evidenced by
git grep "IsHMMove2"
, the function is not used anywhere else, so y'all can safely remove it if you want to.
Male
Europe
Seen March 20th, 2023
Posted January 12th, 2023
271 posts
13.4 Years
If you want to remove the contest movs tab in the summary screen (and being able to easily restore them later) you just have to modify src/pokemon_summary_screen.c
In the function ShowPokemonSummaryScreen there is this code 3 times:
sMonSummaryScreen->maxPageIndex = PSS_PAGE_COUNT - 1;
We just have to change - 1 to - 2 in all three cases, like this
    case PSS_MODE_BOX:
        sMonSummaryScreen->minPageIndex = 0;
        sMonSummaryScreen->maxPageIndex = PSS_PAGE_COUNT - 2; // It was -1
        break;
    case PSS_MODE_LOCK_MOVES:
        sMonSummaryScreen->minPageIndex = 0;
        sMonSummaryScreen->maxPageIndex = PSS_PAGE_COUNT - 2; // It was -1
        sMonSummaryScreen->lockMovesFlag = TRUE;
        break;
    case PSS_MODE_SELECT_MOVE:
        sMonSummaryScreen->minPageIndex = PSS_PAGE_BATTLE_MOVES;
        sMonSummaryScreen->maxPageIndex = PSS_PAGE_COUNT - 2; // It was -1
        sMonSummaryScreen->lockMonFlag = TRUE;
        break;
Male
Seen 14 Hours Ago
Posted October 27th, 2022
238 posts
7.2 Years
[Pokeemerald] Disable Catching Pokemon & Disable Bag Use In Battle

These are pretty simple. Each uses a flag to disable the respective feature. The wiki posts are linked.

Be sure to clear the flags in Overworld_ResetStateAfterWhiteOut if you want them to be cleared when you lose a battle!

Disable Bag Use:


Disable Catching:
Male
Seen 14 Hours Ago
Posted October 27th, 2022
238 posts
7.2 Years
[Pokeemerald] Fix Surf Dismount Ground Effects

This fixes a pretty subtle oversight by Gamefreak - when dismounting a surf blob, the player's ground effects are not updated, which can look weird.

Before: and After:


It's a very simple fix. Here's the wiki post on how to fix it.
Seen March 4th, 2023
Posted August 23rd, 2022
24 posts
5.1 Years

Back in July 2018, Sagiri made an implementation of Evolution Moves for Pokémon Fire Red via C Injection.
Not so long ago, UltimaSoul ported it to Pokeemerald.
Today I got their permission to drop over here a commit of mine, showing pretty quickly that implementation of Evolution Moves as an alternative to Buffel's.

It's nothing particularly complicated, just 3 simple changes and then it's a matter of setting the Evolution Moves in the src/data/pokemon/level_up_learnsets.h file.
https://github.com/LOuroboros/pokeemerald/commit/b67b5bdd1d5d893c1cbe26386658094a8d1f3f84

Quick demonstration:


And that's pretty much it.
I'm having some troubles installing this one (which I see is on the pokeemerald wiki as well). Every time I use make, the compiler keeps giving me the error "request for member 'level' (and 'move') in something not a structure or union". What might be causing this? Thanks in advance

Lunos

Random Uruguayan User

Male
Montevideo (Uruguay)
Seen 10 Hours Ago
Posted 1 Day Ago
3,006 posts
14.7 Years
I'm having some troubles installing this one (which I see is on the pokeemerald wiki as well). Every time I use make, the compiler keeps giving me the error "request for member 'level' (and 'move') in something not a structure or union". What might be causing this? Thanks in advance
I can't apologize enough. This modification seems to be only working on DizzyEgg's battle_engine_v2 right now.
I'm not sure if I forgot to test it myself on a clean repository, or if there's been any changes since I posted it that were done to Pret's repository that affect its functioning.
Regardless, I'll look into it asap.

EDIT: Alrighty, it's ready. Thanks to ExpoSeed's help, I was able to get things to work in a clean copy of Pokeemerald.
Here's the relevant commit: https://github.com/LOuroboros/pokeemerald/commit/d5f48a1af85be80f01ff2c3adde121db924727d1

I'll add the link to it in my original post asap, and Expo will probably edit the article on the wiki very soon too, since that's about modifications to clean Pokeemerald.
Male
Seen July 18th, 2021
Posted August 6th, 2020
16 posts
10.1 Years
Show Type Effectiveness In-Battle [EM]


Now with Doubles, Flying Press/two-typed moves, and Inverse Battles support!

First thing you're going to want to do is to go into graphics\battle_interface, move text.pal to somewhere safe (I don't think the edits I made to this file harm anything, but better to be safe than sorry) and delete text.gbapal and text.gbapal.lz. Now, download the attached text.zip file, and move its contents (which is just a new text.pal) into the graphics\battle_interface folder.

Now, we get to the fun coding part!
Open up src\battle_message.c, search for sTextOnWindowsInfo_Normal, and add this to the bottom of this array:
Spoiler:

{ // 24 "type" super-effective
	.fillValue = PIXEL_FILL(0xE),
	.fontId = 7,
	.x = 0,
	.y = 1,
	.letterSpacing = 0,
	.lineSpacing = 0,
	.speed = 0,
	.fgColor = 6,
	.bgColor = 14,
	.shadowColor = 5,
},
{ // 25 "type" not very effective
	.fillValue = PIXEL_FILL(0xE),
	.fontId = 7,
	.x = 0,
	.y = 1,
	.letterSpacing = 0,
	.lineSpacing = 0,
	.speed = 0,
	.fgColor = 1,
	.bgColor = 14,
	.shadowColor = 3,
},
{ // 26 "type" no effect
	.fillValue = PIXEL_FILL(0xE),
	.fontId = 7,
	.x = 0,
	.y = 1,
	.letterSpacing = 0,
	.lineSpacing = 0,
	.speed = 0,
	.fgColor = 11,
	.bgColor = 14,
	.shadowColor = 11,
},


Now, open src\battle_bg.c, and add this to the bottom of gStandardBattleWindowTemplates (make sure you paste this before DUMMY_WIN_TEMPLATE, though):
Spoiler:

{ // super effective
	.bg = 0,
	.tilemapLeft = 21,
	.tilemapTop = 57,
	.width = 8,
	.height = 2,
	.paletteNum = 5,
	.baseBlock = 0x02a0,
},
{ // not very effective
	.bg = 0,
	.tilemapLeft = 21,
	.tilemapTop = 57,
	.width = 8,
	.height = 2,
	.paletteNum = 5,
	.baseBlock = 0x02a0,
},
{ // no effect
	.bg = 0,
	.tilemapLeft = 21,
	.tilemapTop = 57,
	.width = 8,
	.height = 2,
	.paletteNum = 5,
	.baseBlock = 0x02a0,
},


The final bit for this code belongs in src\battle_controller_player.c.
We're going to start by adding this define at the top of the file:
#include "event_data.h"
This is necessary for getting the Inverse Battle flag later on.

Next, define a new method at the top of the file. You can place it anywhere, but I searched for:
static void MoveSelectionDisplayMoveType(void);
And placed it above it. The method we are defining is:
static void MoveSelectionDisplayMoveTypeDoubles(u8 targetId);
The reason we're defining a whole new method for this here is because it's a bit easier than adding a 1 to every MoveSelectionDisplayMoveType call that happens in this file. If you wanted to, you could replace every MoveSelectionDisplayMoveType(); in the file to MoveSelectionDisplayMoveType(1); and replace the MoveSelectionDisplayMoveType method with the MoveSelectionDisplayMoveTypeDoubles method from later in this post and everything should work the same.

Now, we're going to be copying over the effectiveness table from src\battle_util.c of DizzyEgg's Battle Engine.
I placed this toward the top of the file, under "unknown unused data".
Spoiler:

#define X UQ_4_12

static const u16 sTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES] =
{
	//   normal  fight   flying  poison  ground  rock    bug     ghost   steel   mystery fire    water   grass  electric psychic ice     dragon  dark    fairy
		{X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(0.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0)}, // normal
		{X(2.0), X(1.0), X(0.5), X(0.5), X(1.0), X(2.0), X(0.5), X(0.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(2.0), X(1.0), X(2.0), X(0.5)}, // fight
		{X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(0.5), X(2.0), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(2.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0)}, // flying
		{X(1.0), X(1.0), X(1.0), X(0.5), X(0.5), X(0.5), X(1.0), X(0.5), X(0.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0)}, // poison
		{X(1.0), X(1.0), X(0.0), X(2.0), X(1.0), X(2.0), X(0.5), X(1.0), X(2.0), X(1.0), X(2.0), X(1.0), X(0.5), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0)}, // ground
		{X(1.0), X(0.5), X(2.0), X(1.0), X(0.5), X(1.0), X(2.0), X(1.0), X(0.5), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0)}, // rock
		{X(1.0), X(0.5), X(0.5), X(0.5), X(1.0), X(1.0), X(1.0), X(0.5), X(0.5), X(1.0), X(0.5), X(1.0), X(2.0), X(1.0), X(2.0), X(1.0), X(1.0), X(2.0), X(0.5)}, // bug
		{X(0.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(0.5), X(1.0)}, // ghost
		{X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(0.5), X(1.0), X(0.5), X(0.5), X(1.0), X(0.5), X(1.0), X(2.0), X(1.0), X(1.0), X(2.0)}, // steel
		{X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0)}, // mystery
		{X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(2.0), X(1.0), X(2.0), X(1.0), X(0.5), X(0.5), X(2.0), X(1.0), X(1.0), X(2.0), X(0.5), X(1.0), X(1.0)}, // fire
		{X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(0.5), X(0.5), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0)}, // water
		{X(1.0), X(1.0), X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), X(1.0), X(0.5), X(1.0), X(0.5), X(2.0), X(0.5), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0)}, // grass
		{X(1.0), X(1.0), X(2.0), X(1.0), X(0.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(0.5), X(0.5), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0)}, // electric
		{X(1.0), X(2.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0), X(0.0), X(1.0)}, // psychic
		{X(1.0), X(1.0), X(2.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(0.5), X(0.5), X(2.0), X(1.0), X(1.0), X(0.5), X(2.0), X(1.0), X(1.0)}, // ice
		{X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(0.0)}, // dragon
		{X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(0.5), X(0.5)}, // dark
		{X(1.0), X(2.0), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(2.0), X(1.0)}, // fairy
};

#undef X

(If you're not using DizzyEgg's Battle Engine for whatever reason, just eliminate the last line and the last column of this table to get rid of Fairy and it should work fine.)

Now, search for MoveSelectionDisplayMoveType(void), and add these three methods above it:
Spoiler:

static void MulModifier(u16 *modifier, u16 val)
{
	*modifier = UQ_4_12_TO_INT((*modifier * val) + UQ_4_12_ROUND);
}

u8 TypeEffectiveness(struct ChooseMoveStruct *moveInfo, u8 targetId)
{
	bool8 isInverse = (B_FLAG_INVERSE_BATTLE != 0 && FlagGet(B_FLAG_INVERSE_BATTLE)) ? TRUE : FALSE;
	
	if (gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].power == 0)
		return 10;
	else
	{
		u16 mod = sTypeEffectivenessTable[gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].type][gBattleMons[targetId].type1];

		if (gBattleMons[targetId].type2 != gBattleMons[targetId].type1)
		{
			u16 mod2 = sTypeEffectivenessTable[gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].type][gBattleMons[targetId].type2];
			MulModifier(&mod, mod2);
		}

		if (gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].effect == EFFECT_TWO_TYPED_MOVE)
		{
			u16 mod3 = sTypeEffectivenessTable[gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].argument][gBattleMons[targetId].type1];
			MulModifier(&mod, mod3);

			if (gBattleMons[targetId].type2 != gBattleMons[targetId].type1)
			{
				u16 mod4 = sTypeEffectivenessTable[gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].argument][gBattleMons[targetId].type2];
				MulModifier(&mod, mod4);
			}
		}

		// 10 - normal effectiveness
		// 24 - super effective
		// 25 - not very effective
		// 26 - no effect

		if (mod == UQ_4_12(0.0)) {
			if(isInverse)
				return 24;
			else
				return 26;
		}
		else if (mod <= UQ_4_12(0.5)) {
			if(isInverse)
				return 24;
			else
				return 25;
		}
		else if (mod >= UQ_4_12(2.0)) {
			if(isInverse)
				return 25;
			else
				return 24;
		}
		else
			return 10;
	}
}

static void MoveSelectionDisplayMoveTypeDoubles(u8 targetId)
{
	u8 *txtPtr;
	struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[gActiveBattler][4]);

	txtPtr = StringCopy(gDisplayedStringBattle, gText_MoveInterfaceType);
	txtPtr[0] = EXT_CTRL_CODE_BEGIN;
	txtPtr++;
	txtPtr[0] = 6;
	txtPtr++;
	txtPtr[0] = 1;
	txtPtr++;

	StringCopy(txtPtr, gTypeNames[gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].type]);
	BattlePutTextOnWindow(gDisplayedStringBattle, TypeEffectiveness(moveInfo, targetId));
}

Obviously, you can remove the whole two-type move check if you're not using DizzyEgg's Battle Engine.

And in MoveSelectionDisplayMoveType, change this line:
BattlePutTextOnWindow(gDisplayedStringBattle, 10);
To this:
BattlePutTextOnWindow(gDisplayedStringBattle, TypeEffectiveness(moveInfo, 1));
Now, we just need to add calls for MoveSelectionDisplayMoveTypeDoubles. Search for HandleInputChooseTarget(void) and find the two places that use this switch statement:
switch (GetBattlerPosition(gMultiUsePlayerCursor))
Immediately after these switch statements, add this line:
MoveSelectionDisplayMoveTypeDoubles(GetBattlerPosition(gMultiUsePlayerCursor));
And you should be good to go!
Thanks to DizzyEgg for his hard work on the Battle Engine upgrade. The type effectiveness table and the MulModifier method made things very easy once I figured out how the palettes worked. I for sure would not have been able to do this without those.
For me, there was a missing step in this really good tutorial. In one of the newly added functions, TypeEffectiveness, EFFECT_TWO_TYPED_MOVE was undeclared. This made it so that I could not compile the game after modifications. The solution I found was to include battle_move_effects.h in the battle_controller_player.c file. Additionally, event_data.h should be included towards the end of the include section. This was not clear in the tutorial, as you said to add it at the top. This may all be obvious for more experienced people in the community, but for me just starting out, it took a little while to track down. Thank you for this great tutorial, all of the resources posted here give a lot of inspiration for what can be accomplished.
Seen March 13th, 2023
Posted July 30th, 2020
1 posts
3 Years
Fixing Battle Factory opponent IV Glitch
A little known glitch is in the Battle Factory, the IVs of the opponent are based off your progress in the Battle Tower instead of the Battle Factory, so for instance if you just got a gold medal in the Battle Tower then started in the Battle Factory, you would start off with pokemon that have IVs of 3 while your opponent would have IVs of 31, this is easily fixed.

Go into src\battle_tower.c

Go to the function, around line 1824 for me
static void FillFactoryFrontierTrainerParty(u16 trainerId, u8 firstMonId)
About 12 lines down down you should see
u8 challengeNum = gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][0] / 7;
change this to
u8 challengeNum = gSaveBlock2Ptr->frontier.factoryWinStreaks[battleMode][lvlMode] / 7;
And you're done, the IVs of the opponent pokemon should now match your progress within the Battle Factory correctly.
Age 25
Male
Seen December 31st, 2021
Posted October 10th, 2021
146 posts
15.5 Years
For me, there was a missing step in this really good tutorial. In one of the newly added functions, TypeEffectiveness, EFFECT_TWO_TYPED_MOVE was undeclared. This made it so that I could not compile the game after modifications. The solution I found was to include battle_move_effects.h in the battle_controller_player.c file. Additionally, event_data.h should be included towards the end of the include section. This was not clear in the tutorial, as you said to add it at the top. This may all be obvious for more experienced people in the community, but for me just starting out, it took a little while to track down. Thank you for this great tutorial, all of the resources posted here give a lot of inspiration for what can be accomplished.
Oh huh, guess I totally forgot the battle_move_effects include. Thanks!