PHO - Pokémon Hackers Online
Go Back   PHO - Pokémon Hackers Online > Discussion Board > Archive

Notices

Archive Old threads that serve no purpose are here.

 
 
Thread Tools Display Modes
Old 13th May 2014, 03:03 PM   #1
daniilS
VIP
PHO VIP
 
Join Date: Aug 2013
Posts: 59
daniilS
Default Pokéball hacking

Pokéball Hacking Guide


Introduction
Some months ago, when I was still attempting my first hack, I was wondering whether it was possible to create a custom pokeball. The only thing I found was this post by JPAN. It wasn't complete, and I hadn't seen anyone using it, so I forgot about it.
Now, I am working full-time on Platinum Red&Blue with GoGoJJTech, and had to implement gen IV pokeballs into the hack. I have altered JPAN's asm code a bit, and included documentation on the throwing graphics for everybody to use. Enjoy!


Requirements
  • A hex editor (I use Hex Workshop)
  • A way to find free space in your rom; FSF recommended
  • Images for your balls
  • A way to insert those images (I use nse 2.1)
  • Basic hex editing/assembly knowledge. I do assume you know how to assemble a routine and place an inverted pointer +1 to it.


Step 1: Dealing with the pokemon data limiters
This is the easiest part. As you can see here, the balls only get four bits of data, and 0 is unused, meaning you can add up to three balls. Since I don't think there are many people who have transferred pokemon from Colosseum or XD to a hack, it's safe to sacrifice the most significant bit used to determine the game of origin, leaving us space for 19 extra balls. If you need more, you will need a different workaround.
You've got to change six bytes:
  • At 080400B6, place 80
  • At 080400B8, place 44
  • At 080400BE, place 0B
  • At 08040855, place FC
  • At 0804085A, place 1F
  • At 0804085E, place 89
  • At 08040862, place 7C
Spoiler:
IMPORTANT: If your hack is already released, this will screw up pokemon already in pokeballs: they will all get the ball*2, f.e. a poke in a pokeball will become a poke in a friend ball. This may look like a minor bug at first, but if the ball index is high, the doubled index may not exist, resulting in a crash upon throwing the ball. So, if your hack is already released, you can do three things:
  • Force the players to restart the save or not to use pokemon caught in balls with high indexes previously
  • Only add up to three balls
  • Expand the data like mentioned, and then double all existing ball indexes. Then inflate the catch rate table at part 3 and the conversion table at step four part 1. So instead of [original entry 1][original entry 2]...[entry 12][custom entry 1][custom entry 2]..., they will become [custom/empty entry 1][original entry 1][custom/empty entry 2][original entry 2][custom/empty entry 3][original entry 3]. That should fix the problem.

And you're done! Move on to step 2.


Step 2: Inserting the catch rate routine
JPAN has written a neat routine that replaces the regular check for pokeballs (see the link posted above). However, it requires you to perform a cmp for every ball index, and that's not very efficient and forces you to modify the routine every time you add a ball. Thus, I have modified it, so that it checks what pocket the selected item is in. Now, you only have to give the item the right pocket value and it becomes a ball. The routine:
Spoiler:
Code:
.text
.align 2
.thumb
.thumb_func
.global ballchecker

ballcompare:
	mov r0, #0x2C
	mul r0, r1
	mov r10, r1
	ldr r1, itemdata
	add r1, r0, r1
	add r1, #0x1A
	ldrb r0, [r1]
	cmp r0, #0x3
	beq isball
otherstuffcompare:
	mov r1, r10
	cmp r1, #0x50
	beq dolly
	cmp r1, #0x51
	beq dolly
	mov r0, #0xaf
	lsl r0, r0, #0x1
	cmp r1, r0
	beq flute
	ldr r0, return_addr
	bx r0 
isball:
	ldr r0, somevar
	add r1, #0x1
	ldrb r1, [r1]
	strb r1, [r0]
	ldr r0, ball_script
	cmp r1, #0x11	/*If you want to have a second safari ball like the park ball, place its pokeball index here*/
	beq safari	/*Otherwise, remove these two lines*/
	cmp r1, #0x5
	bne preender
safari:
	add r0, #0x28
preender:
	b script_ender
dolly:
	ldr r0, doll_script
	b script_ender
flute:
	ldr r0, flute_script
script_ender:
	ldr r1, script_ptr
	str r0, [r1]
	ldr r1, be3_addr
	mov r10, r1
	ldr r0, end_addr
	bx r0

.align 2
	ball_script:		.word 0x081D9A14
	doll_script:		.word 0x081D9B7C
	flute_script:		.word 0x081D9B86
	script_ptr:		.word 0x02023d74
	be3_addr:		.word 0x02023be3
	return_addr:		.word 0x080164e9
	end_addr:		.word 0x0801671f
	itemdata:		.word 0x083DB028
	somevar:		.word 0x0202fe00

Now, in the code you see "somevar". There you must place an address (or use this one I saw in JPAN's original routine) that will keep track of the current pokeball index. Other than in the routine, there are a few places you need to place a pointer to it:
  • 0802D500
  • 0802D544
  • 0802D6A0
  • 0802D71C
  • 0802D7B8
  • 080EF4B4
  • 080EF4DC
  • 080EF674
  • 080EF9AC
  • 080F0368
You probably don't know what the pokeball index is. Well, it's an unused value in the item structure, and afaik you can only change it with the tool "Item manager". Sadly, it doesn't have ini support, so if you repointed the items, do this:
Take the index number of the item, multiply it by 0x2C, then add to it the offset of the item data and finally, add to that 0x1B, and you have the pokeball index field. Using either method, you need to put there the pokeball number. For regular balls, it is equal to the index number; for custom balls, just continue counting up. Also, set your balls' field usage to 0, battle usage to 2, and code to 80A1E1D. (more information here)
Now you've done all that, it's time to assemble the routine. Insert in anywhere, and place a pointer to it at 08016494. Then at 08016460, type 00 00 00 00 0B 4A 97 46. Done! Move on to the next step to make your ball actually do something.


Step 3: Making the balls do something
The balls with a pokeball index (previously index number) higher than 5 each have their own catch rate calculation subroutine. We need to extend that. To do this, first change the byte at 0802D52C to FF. Then, look at the table at 0802D54C. It has a pointer to a routine for every pokeball starting from 6. At 0802D548 you can see a pointer to the table; repoint it somewhere else. Now, to give a ball a certain catch rate, you must write a routine to load it into r4, and exit to 0802D7A2. Then place a pointer to that routine in the table.
Some example routines:
Quick ball:
Spoiler:
Code:
.text
.align 2
.thumb
.thumb_func
.global catchratebyturn

main:
	mov r4, #10
	ldr r0, battleturncounter
	ldrb r0, [r0]
	cmp r0, #0x0
	bne ender
firstturn:
	add r4, #30
ender:
	ldr r0, catchratecalcfunction
	mov pc, r0
	
.align 2
	battleturncounter:	.word 0x03004fa3
	catchratecalcfunction:	.word 0x0802d62a

Dusk ball:
Spoiler:
Code:
.text
.align 2
.thumb
.thumb_func
.global catchratebyareaortimeofday

main:
	ldr r0, time
	ldrb r0, [r0, #0x0]
	cmp r0, #0x6
	blt night
	cmp r0, #0x15
	bge night
day:
	mov r4, #10
	ldr r0, maptype
	ldrb r0, [r0]
	cmp r0, #0x4
	bne ender
night:
	mov r4, #35
ender:
	ldr r0, catchratecalcfunction
	mov pc, r0

.align 2
	time:			.word 0x03005542
	maptype:		.word 0x02036e13
	catchratecalcfunction:	.word 0x0802d62a

Park ball; this one uses a modified safari ball routine, and will get another piece of code later:
Spoiler:
Code:
.text
.align 2
.thumb
.thumb_func
.global safaricatchmax

main:
	LDR     R0, dword_2023FE8
	LDR     R0, [R0]
	ADD     R0, #0x7C
	LDR     R0, [R0]
	LSL     R1, R0, #2
	ADD     R1, R1, R0
	LSL     R0, R1, #8
	SUB     R0, R0, R1
	MOV     R1, #0x64
	BL      __divsi3
	LSL     R0, R0, #0x18
	LSR     R5, R0, #0x18
park:
	mov r4, #0xff
	ldr r0, catchratecalcfunction
	mov pc, r0
	
.align 2
	catchratecalcfunction:	.word 0x0802D62A
	dword_2023FE8:		.word 0x02023FE8

Heal ball/Cherish ball (the heal ball will get another piece of code later)
These are special. They load the catch rate from the "Extra value" of the item (add 0x28 to the item offset and change the word you find there). In the case of these balls it's 10, but you can easily change it without altering the code:
Spoiler:
Code:
.text
.align 2
.thumb
.thumb_func
.global staticcatchrate

main:
	ldr r4, itembuffer
	ldrh r4, [r4]
	mov r0, #44
	mul r4, r0
	ldr r0, itemdata
	add r0, r4
	add r0, #40
	ldr r4, [r0]
	ldr r0, catchratecalcfunction
	mov pc, r0
	
.align 2
	catchratecalcfunction:	.word 0x0802d62a
	itemdata:		.word 0x083DB028
	itembuffer:		.word 0x02023d68

After inserting the routines, take your ball index, substract six of it, multiply by four, and add to the new offset of the table. Place the offset where you inserted your routine there.
Here comes the part JPAN didn't document: animations. And be prepared, it's a long part.


Step four: Expanding the throwing graphics part 1
The conversion table
Now, if you've ever looked at the pokeballs in an item viewer and at the throwing graphics of them in an image viewer, you might have noticed they're in a different order. So, there is a routine somewhere that converts the index to the throwing graphics table (or tgt in short because I'm lazy) entry. That routine is located at 080EF52C. Due to the way it's written, there's enough space to replace it by a new routine and a table. First the routine:
Spoiler:
Code:
.text
.align 2
.thumb
.thumb_func
.global ballchecker

main:
	push {lr}
	ldr r1, throwtablepositionstable
	add r0, r0, r1
	ldrb r0, [r0]
	pop {pc}

.align 2
	throwtablepositionstable:	.word 0x080EF53C

Assemble this routine and insert it at 080ef52C. Now, the conversion from index to tgt entry will be done through throwtablepositionstable. By this, we freed up more than enough space at the specified offset, so I recommend you to put the table there. Now for the table, it will look like this:
Code:
(unused zeroth entry) FF 04 03 01 00 02 05 06 07 08 09 0A 0B (insert ongoing numbers for each of your balls here)
It is the easiest if you insert the throwing graphics in the same order as your pokeballs, so just count up. For the five gen 4 balls, it would be
Code:
FF 04 03 01 00 02 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
But be careful: while the pokeball index was one-indexed, the tgt is zero-indexed, which means that in the entry for ball x you will have to type x-1.
After inserting this, remove a limiter by typing 00 00 at 080EF972 and at 080F02D6, and continue to the next step.


Step four: Expanding the throwing graphics part 2
The image and palette tables
Prepare your ball images. Example with gen 4 balls:
Made by Bela. Isn't he awesome?
Anyways, you must repoint and extend two tables in this step. First, the one for the images, located at 0826056C. 12 entries, each eight bytes: one word and two halfwords. Repoint it somewhere. Then, append your own balls as follows:
Code:
word		pointer to image
halfword	decompressed size (180)
halfword	tag of ball(I'm not sure which tags are free exactly, but I started with tag 0xFF00 for the first entry and counted up for the following balls/particles and it worked fine.)
The second table is a lot like this one: it's for the palettes. Located at 082605CC, again 12*4 bytes long. Repoint somewhere, and extend this way:
Code:
word		pointer to palette
halfword	tag of ball
halfword	filler (0000)
We just started repointing, and we still have a long way to go.


Step four: Expanding the throwing graphics part 3
Repointing generic tables
Here comes a list of the tables:
Spoiler:

Code:
082606F4	ball object template table
one entry is 24 bytes:
halfword	tag of ball
halfword	tag of ball
word		0826062C
word		08260674
word		00000000
word		082606E0
word		0804ABD5
Code:
0840BF48	unknown particle table
one entry is 8 bytes:
word		08D1A608
halfword	0100
halfword	tag of particle
Code:
0840BFA8	unknown particle table 2
one entry is 8 bytes:
word		08D1A6DC
halfword	tag of particle
halfword	0000
Code:
0840C0A4	particle object template table
one entry is 24 bytes:
halfword	tag of particle
halfword	tag of particle
word		083AC9C8
word		0840C050
word		00000000
word		08231CFC
word		0800760D
After you have done this, you can finally assign particles and animations to your balls.


Step four: Expanding the throwing graphics part 4
The particle and animation tables
As for now, I will only document the tables and their meanings, and how to assign existing animations/particles to new balls. Another part on custom animations may follow later.
The first table you need to repoint and extend is located at 0840C068. For every ball it tells which particle to use. 0=sticks, 1=stars, 2=bubbles, 3=hearts, 4=big green crosses, 5=small green crosses (last two strongly affected by the second table).
The second table is located at 0840C074. Each entry is a pointer to a routine+1 that describes how the particles should act. I can't really describe the effects, so just look at them.
And that's it! Congratulations, you've succesfully expanded the pokeballs! The next parts will describe how to apply some special effects.


Step five: Additional effects part 1
Healing pokemon after capture
This effect was written by me for the Heal Ball:
Spoiler:
Code:
.text
.align 2
.thumb
.thumb_func
.global afterpoketranscalc

main:
	push {lr}
recycle:
	ldr r3, memcpy
	bl bxr3
calc:
	cmp r7, #0x64
	bne ender
	ldr r3, somevar
	ldrb r3, [r3]
	cmp r3, #14	/*ball index of heal ball here*/
	bne ender
healstuff:
	ldr r3, poke_quantity
	ldrb r1, [r3]
	mov r10, r1
	mov r1, #1
	strb r1, [r3]
heal_recycle:
	mov r3, pc
	add r3, #0x1d
	push {r3}
	push {r4-r7}
	mov r7, r10
	mov r6, r9
	mov r5, r8
	push {r5-r7}
	sub sp, sp, #4
	mov r1, #0
	mov r8, r1
	mov r1, r0
	mov r10, r1
	mov r6, sp
	ldr r3, healpoke
	b bxr3
heal_finish:
	ldr r3, poke_quantity
	mov r1, r10
	strb r1, [r3]
ender:
	pop {r3}
bxr3:
	bx r3

.align 2
	memcpy:			.word 0x081e5e78+1
	somevar:		.word 0x0202fe00
	partyadr:		.word 0x02024284
	poke_quantity:		.word 0x02024029
	healpoke:		.word 0x080a0076+1
In this code you must change the values at the ball index and somevar. Then assemble it and insert it anywhere. To activate it, place 00 4B 9F 46 XX XX XX XX at 08040B08, where XXXXXXXX is an inverted pointer to this routine.


Step five: Additional effects part 2
Retaining pokeballs
This effect was written by me for the Park Ball, and will work if the caught poke already has a pokeball, like with the Pal Park:
Spoiler:
Code:
.text
.align 2
.thumb
.thumb_func
.global keepballcheck

main:
	mov r5, r3
	ldr r3, returnadr
	cmp r5, #17
	bne bxr3
	add r3, #4
bxr3:
	bx r3

.align 2
	returnadr:				.word 0x0802D7A2+1
In this code you must only change the the ball index. Then assemble it and insert it anywhere. To activate it, place 40 4B 9F 46 12 F0 EB FD 13 F0 49 FA at 0802D6EE, and 70 BD XX XX XX XX at 0802D7EE where XXXXXXXX is an inverted pointer to this routine.


Step five: Additional effects part 3
The givepokemon hack
One of the gen 4 balls is the Cherish Ball, which has no effects other than being the standard ball for event pokes. To copy this effect, I wrote a piece of code to change the givepokemon command. Know how it has three unused parameters? Well, the last one, a byte, is now the pokeball the received pokemon will be in. If it's 0, it will be in a regular pokeball.
Anyways, the hack:
Code:
Type A1 68 0A 79 00 2A 00 D1 04 22 05 31 at 0806c014. Activate the hack by typing 29 98 at 0803DC52.
Congratulations! You are finished! If you write more ball effects, I'm sure everybody will gladly take a look at them!


To-do list
  • Implement custom particles
  • Discover how to write custom animations
  • Come up with a solution for the Heavy Ball
  • Think of a way to create a ball that catches trainer's pokemon
  • Finish the givepokemon hack to include the moveset


Credits
  • JPAN, for the original research and his tutorial that actually got me into asm
  • knizz, for his disassembly (if you haven't heard about it yet, it's a must-have for any asm hacker: link) and infinite amount of help, with finding tables, answering all my dumb questions, and even answering most of my advanced questions
  • GoGo, for pushing me to finish this
  • Bela, for drawing the balls
  • Jambo51, for the idea of the givepokemon hack
  • Mat (bond697), for trying to help me fix a major issue with this. Although the reason for the crash turned out to be something else, I appreciate his help and I learned something


Last edited by daniilS; 15th May 2014 at 06:21 PM. Reason: Added extra info for hacks in progress and fixed a mistake and fixed another one
daniilS is offline  
Likes Bela, Elsa, Full Metal ★, Icey, Cruztown, soulryu liked this post
Sponsored Links
Old 14th May 2014, 02:16 PM   #2
Pia Carrot
Orange Developer
Administrator
 
Pia Carrot's Avatar
 
Join Date: Aug 2010
Location: Valencia Island
Age: 23
Posts: 811
Pia Carrot Pia Carrot Pia Carrot Pia Carrot Pia Carrot
Default

Glad somebody started delving into this, I am sure lots of great new Pokeballs could come out of this.
Pia Carrot is offline  
Likes daniilS liked this post
Old 14th May 2014, 11:46 PM   #3
Elsa
Let it go!
PHO VIP
 
Elsa's Avatar
 
Join Date: Apr 2010
Location: United States
Age: 21
Posts: 365
Elsa
Default

Really great tutorial! I love the CSS in the thread and also that you not only showed how to add pokeballs, but also explained how to add the graphics and code. Very well done.
__________________

- - - - - - - Elsa - - - - - - -
Twitter | YouTube | Steam | Twitch
- - - - - - - - - - - - - - - - -
Elsa is offline  
Likes daniilS liked this post
 

Tags
ball, hacking, pokeball, pokeballs, [Guide]

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT. The time now is 06:54 AM.

Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2018, vBulletin Solutions, Inc. User Alert System provided by Advanced User Tagging (Lite) - vBulletin Mods & Addons Copyright © 2018 DragonByte Technologies Ltd.
Feedback Buttons provided by Advanced Post Thanks / Like (Lite) - vBulletin Mods & Addons Copyright © 2018 DragonByte Technologies Ltd.
Pokémon characters and images belong to Pokémon USA, Inc. and Nintendo.
Pokémon Hackers Online (PHO) is in no way affiliated with or endorsed by Nintendo LLC, Creatures, GAMEFREAK inc,
The Pokémon Company, Pokémon USA, Inc., The Pokémon Company International, or Wizards of the Coast.
All forum/site content (unless noted otherwise) and site designs are © 2006-2013 Pokémon Hackers Online (PHO).
Green Charizard Christos TreeckoLv100

"Black 2" by ARTPOP. Kyurem artwork by XOUS.

no new posts