• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Forum moderator applications are now open! Click here for details.
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

Development: Uncatchable Pokemon

Mr.Pkmn

Ordinary ASM Magician
53
Posts
15
Years
  • Seen Nov 17, 2023
NintendoBoyDX, In the memory viewer please set to 8bit, so you can see the offsets of my table.
bdf3834a998dbbc4a5e1faa5d66266f0.png


Thats basically what you can mess with the battles.

Hope this helps, because i want this feature too, for my own researchs.

~Sonic1
I wonder if with this we could make use of the third parameter of the trainerbattle command (used for oak's tutorial). It would allow battle tower-like trainers in fire red...
 

ShadowMrk

Intangible
70
Posts
13
Years
  • Age 28
  • Seen Nov 27, 2018
It seems to me that you guys are drawing this out longer than it has to be. If all you want is the Pokemon to be uncatchable then just do this:

-First, find the place in RAM that holds the current wild Pokemon's catch rate and find at what place it stores that data into a register.

-Then make a check right before the game calculates the catch rate for a certain address that isn't used for anything that you can use to see whether or not this battle is of the uncatchable type.

-If that variable IS set then jump to a custom routine that sets the temporary catch rate to 0 and stores the original catch rate into another unused variable.

-Then make another jump after the catch rate calculation that sets the temporary catch rate back to the original catch rate.

-Then make two more routines, one to set the uncatchable variable and another to clear it. Then use those routines in a script before and after an important wild Pokemon battle. Tada! You don't even have to touch the Ghost Marowak ASM.

It was late at night when I wrote this so if I missed something please tell me. Also, if the game doesn't store the catch rate into a temporary location (which is doubtful that they wouldn't) then doing this would be alot harder ,but not impossible.
 

Jambo51

Glory To Arstotzka
736
Posts
14
Years
  • Seen Jan 28, 2018
It seems to me that you guys are drawing this out longer than it has to be. If all you want is the Pokemon to be uncatchable then just do this:

-First, find the place in RAM that holds the current wild Pokemon's catch rate and find at what place it stores that data into a register.

-Then make a check right before the game calculates the catch rate for a certain address that isn't used for anything that you can use to see whether or not this battle is of the uncatchable type.

-If that variable IS set then jump to a custom routine that sets the temporary catch rate to 0 and stores the original catch rate into another unused variable.

-Then make another jump after the catch rate calculation that sets the temporary catch rate back to the original catch rate.

-Then make two more routines, one to set the uncatchable variable and another to clear it. Then use those routines in a script before and after an important wild Pokemon battle. Tada! You don't even have to touch the Ghost Marowak ASM.

It was late at night when I wrote this so if I missed something please tell me. Also, if the game doesn't store the catch rate into a temporary location (which is doubtful that they wouldn't) then doing this would be alot harder ,but not impossible.

While this would work, it's not what the OP actually wants.

I've managed to track down what I believe to be the ghost check for the Pokéball throw. This could be a useful lead.
 

metapod23

Hardened Trainer
673
Posts
15
Years
  • Seen Aug 18, 2016
While this would work, it's not what the OP actually wants.

I've managed to track down what I believe to be the ghost check for the Pokéball throw. This could be a useful lead.

Awesome. I really appreciate you looking into it. :cool:
 

ShadowMrk

Intangible
70
Posts
13
Years
  • Age 28
  • Seen Nov 27, 2018
While this would work, it's not what the OP actually wants.

I've managed to track down what I believe to be the ghost check for the Pokéball throw. This could be a useful lead.

Nonetheless, this has motivated me to do some research on it. If I could the function(s) that display text and a textbox on the screen then we could still do it the way I suggested while giving Metapod what he wants.
 

JPAN

pokemon rom researcher
104
Posts
15
Years
  • Seen Jul 2, 2016
I believe I have the answer to this question.

BattleScript 0xef is the pokeball catch routine. It is used whenever a ball is thrown, and depending on several checks, jumps to the script best equiped to handle the situation. Near the beginning, it makes three checks to check for situations where the outcome is always the same.


Starting at at 0802D452, we have:
  • the ghost battle check; continues to the script at 0x081D9Ad1
  • the trainer battle check; contiu 0x081D9AC1
  • the fake battle check; continues to the script at 0x081D9A88
With a small bypass near that area, we can use the ghost script (that has nothing related to the ghost battle in it) to prevent capture of a pokemon when a certain flag, variable or another address in the RAM has the desired value. The code is presented below:
Code:
Catch_me_not:  ldr r0, num_to_check /*can be anything you want, like flags, variables or addresses. Here, we'll use addresses*/
    ldrb r0, [r0]
    cmp r0, #0x1 /*or any other value you want*/
    bne return_to_normal
    ldr r1, battleFlags
    ldr r1, [r1]
    ldr r0, afterGhostAddr
    bx r0
 
return_to_normal: ldr r1, battleFlags
     ldr r1, [r1]
     ldr r0, ghostCheckAddr
     bx r0
 
num_to_check: .word 0x020370c0 /*var 8004 address, in this example*/
battleFlags: .word 0x02022b4c
afterGhostAddr: .word 0x0802d461
ghostCheckAddr: .word 0x0802d457

In the attachment, you have the compiled version of this code, as well as the source that contains a variable access and a flag access code, commented. For those versions, replace num_to_check with the number of the flag/variable you want.

Note: There was a typo on the commented code, on the Load variable commented function: the address should be 0x0806e569, not 0x0806e568.
 
Last edited:

metapod23

Hardened Trainer
673
Posts
15
Years
  • Seen Aug 18, 2016
I believe I have the answer to this question.

BattleScript 0xef is the pokeball catch routine. It is used whenever a ball is thrown, and depending on several checks, jumps to the script best equiped to handle the situation. Near the beginning, it makes three checks to check for situations where the outcome is always the same.

Starting at at 0802D452, we have:
  • the ghost battle check; continues to the script at 0x081D9Ad1
  • the trainer battle check; contiu 0x081D9AC1
  • the fake battle check; continues to the script at 0x081D9A88
With a small bypass near that area, we can use the ghost script (that has nothing related to the ghost battle in it) to prevent capture of a pokemon when a certain flag, variable or another address in the RAM has the desired value. The code is presented below:
Code:
Catch_me_not:  ldr r0, num_to_check /*can be anything you want, like flags, variables or addresses. Here, we'll use addresses*/
    ldrb r0, [r0]
    cmp r0, #0x1 /*or any other value you want*/
    bne return_to_normal
    ldr r1, battleFlags
    ldr r1, [r1]
    ldr r0, afterGhostAddr
    bx r0
 
return_to_normal: ldr r1, battleFlags
     ldr r1, [r1]
     ldr r0, ghostCheckAddr
     bx r0
 
num_to_check: .word 0x020370c0 /*var 8004 address, in this examle*/
battleFlags: .word 0x02022b4c
afterGhostAddr: .word 0x0802d461
ghostCheckAddr: .word 0x0802d457

In the attachment, you have the compiled version of this code, as well as the source that contains a variable access and a flag access code, commented. For those versions, replace num_to_check with the number of the flag/variable you want.

I tried it out, but haven't gotten it to work. Here's the code I'm using:

Code:
.thumb
.align 2

Catch_me_not: 	ldr r0, =00007015
				bl	LoadVariable
				cmp r0, #0x01
				bne return_to_normal
				ldrb r0, [r0]
				cmp r0, #0x1
				bne return_to_normal
				ldr r1, battleFlags
				ldr r1, [r1]
				ldr r0, afterGhostAddr
				bx r0
				
return_to_normal:	ldr r1, battleFlags
					ldr r1, [r1]
					ldr r0, ghostCheckAddr
					bx r0

battleFlags:	.word 0x02022b4c
afterGhostAddr: .word 0x0802d461
ghostCheckAddr:	.word 0x0802d457


LoadVariable:	ldr r1, ld_Var_addr
				bx r1
ld_Var_addr:	.word 0x0806e568

Unfortunately, the game freezes any time I try to use a Poke Ball at all, whether a variable is set or not, freezing on [player] used Poke Ball!

With the code, I'm trying to use variable 0x7015. I placed the code at offset 0x08874120.

At offset 0x0802D484 where the original bytes were:
Code:
4C 2B 02 02

I replaced them with a pointer to the new code:

Code:
21 41 87 08


And at offset 0x0802D454 where the original bytes were:

Code:
01 68

I replaced them with:

Code:
00 47
 
Last edited:

JPAN

pokemon rom researcher
104
Posts
15
Years
  • Seen Jul 2, 2016
I tried it out, but haven't gotten it to work.
Sorry about that, copy-paste function from an early code that was bugged, the line
Code:
ld_Var_addr: .word 0x0806e56[B]8[/B]
should read
Code:
ld_Var_addr: .word 0x0806e56[B]9[/B]

That way, it should work.
 

metapod23

Hardened Trainer
673
Posts
15
Years
  • Seen Aug 18, 2016
Sorry about that, copy-paste function from an early code that was bugged, the line
Code:
ld_Var_addr: .word 0x0806e56[B]8[/B]
should read
Code:
ld_Var_addr: .word 0x0806e56[B]9[/B]

That way, it should work.

It doesn't freeze, but the Pokemon isn't dodging the Poke Ball for me either when the variable is set. Not sure if it's something I'm doing wrong.
 

ShadowMrk

Intangible
70
Posts
13
Years
  • Age 28
  • Seen Nov 27, 2018
It amazes me how fast JPAN did that. I salute you, JPAN. I have alot to learn... :\
 

metapod23

Hardened Trainer
673
Posts
15
Years
  • Seen Aug 18, 2016
it worked for me, though i used JPAN's already made .out file

Maybe someone can explain to me what's wrong with my code then ... I suspect now that I have more than I need in there, but it still isn't working.

From how I read it, it seems to compare the variable to the value, and if it's not equal, goes to the return_to_normal instruction.

But the instruction following that (if it doesn't take you to return_to_normal) appears to be exactly the same, so I guess I don't understand where the direction is to take you to the uncatchable code if they are equal. It may be that I'm writing it wrong or understanding what I'm supposed to do wrong.
 

JPAN

pokemon rom researcher
104
Posts
15
Years
  • Seen Jul 2, 2016
I tried it out, but haven't gotten it to work. Here's the code I'm using:
Code:
.thumb
.align 2
 
Catch_me_not:     ldr r0, =00007015
                bl    LoadVariable
                cmp r0, #0x01
                bne return_to_normal
                [U]ldrb r0, [r0][/U]
                [U]cmp r0, #0x1[/U]
                [U]bne return_to_normal[/U]
                ldr r1, battleFlags
                ldr r1, [r1]
                ldr r0, afterGhostAddr
                bx r0
 
return_to_normal:    ldr r1, battleFlags
                    ldr r1, [r1]
                    ldr r0, ghostCheckAddr
                    bx r0
 
battleFlags:    .word 0x02022b4c
afterGhostAddr: .word 0x0802d461
ghostCheckAddr:    .word 0x0802d457
 
 
LoadVariable:    ldr r1, ld_Var_addr
                bx r1
ld_Var_addr:    .word 0x0806e569
All the underlined code is what's throwing the code off. The value returned by the variable loader is the value to compare right after. After that, loading from R0 would be loading from address 1, a BIOS address. That address value (if even loaded) will most likely not be 1, so the second check fails.
That second check is used only on the "address reading" version of the code. Clean it off, and it should work.
 

Mr.Pkmn

Ordinary ASM Magician
53
Posts
15
Years
  • Seen Nov 17, 2023
With a small bypass near that area, we can use the ghost script (that has nothing related to the ghost battle in it) to prevent capture of a pokemon when a certain flag, variable or another address in the RAM has the desired value.
Hey JPAN, can you do the same with the trainerbattle command? Making a check to activate the battle tower bit that starts an item forbidden trainer/wild pokemon battle. It's not the same as an uncatchable pokemon, but that would also allow "special", multiplayer-like trainer battles...
 

JPAN

pokemon rom researcher
104
Posts
15
Years
  • Seen Jul 2, 2016
Hey JPAN, can you do the same with the trainerbattle command? Making a check to activate the battle tower bit that starts an item forbidden trainer/wild pokemon battle. It's not the same as an uncatchable pokemon, but that would also allow "special", multiplayer-like trainer battles...
Not sure if it's this, but in the same way we disable the capture, we can disable the item usage. Before the bag is open, a rather large function that deals with input from the Fight-Bag-Party-run box eventually reaches a check for the item-denying battles. At 080143D4, that check starts, and when successful, continues to 0x080143E0, that loads the script to execute. Placing a bypass there, we get a code amost identical to the one for the capture prevention. The code and compiled address version are included in the attachment. Right now, it's loading from 0x020370c0 (var 8004 address), so change it and compile it as you need it.
 

Deokishisu

Mr. Magius
989
Posts
18
Years
^Just what I was about to ask.

Since we have the code for what prevents a capture, can we enable a capture during a trainer battle?

I can already foresee a problem with this though. I'm pretty sure capturing an enemy trainer's Pokemon immediately ends the battle, not to mention the fact that it'd be difficult to make only a certain Pokemon from an enemy trainer's roster catchable.

So I guess the question is not can an opponent's Pokemon be captured, but can it be implemented well?
 

Mr.Pkmn

Ordinary ASM Magician
53
Posts
15
Years
  • Seen Nov 17, 2023
Not sure if it's this, but in the same way we disable the capture, we can disable the item usage. Before the bag is open, a rather large function that deals with input from the Fight-Bag-Party-run box eventually reaches a check for the item-denying battles. At 080143D4, that check starts, and when successful, continues to 0x080143E0, that loads the script to execute. Placing a bypass there, we get a code amost identical to the one for the capture prevention. The code and compiled address version are included in the attachment. Right now, it's loading from 0x020370c0 (var 8004 address), so change it and compile it as you need it.
Crashes when the bag is selected. It works if i put 0x080143d9 as the pointer (but obviously this is permanent).

Also, while it's useful to have only items disabled (eg: wild battle) the other things (no experience gained, no switch prompt after ko) are activated if 0x02022B4D is 0x1, so you could integrate these other routines if the variable has an another value.
 

metapod23

Hardened Trainer
673
Posts
15
Years
  • Seen Aug 18, 2016
All the underlined code is what's throwing the code off. The value returned by the variable loader is the value to compare right after. After that, loading from R0 would be loading from address 1, a BIOS address. That address value (if even loaded) will most likely not be 1, so the second check fails.
That second check is used only on the "address reading" version of the code. Clean it off, and it should work.

I think I finally got it to work! Thanks JPAN! :) I'll credit you for the code in my hack's thread.

One last thing: Isn't there any way to include the poke ball thrown animation and the dodge animation as well? This does the trick, but shouldn't you be able to load the address that plays those animations as well?
 

Mr.Pkmn

Ordinary ASM Magician
53
Posts
15
Years
  • Seen Nov 17, 2023
Crashes when the bag is selected. It works if i put 0x080143d9 as the pointer (but obviously this is permanent).
Uh I've fixed this, now the routine is working using writebytetooffset with a custom ram address to turn off items. Still would like to see a "complete" battle tower-like routine ;)
 
Back
Top