The PokéCommunity Forums  

Go Back   The PokéCommunity Forums > ROM Hacking > Research & Development
Sign Up Rules/FAQ Live Battle Blogs Mark Forums Read

Notices

Research & Development Got a well-founded knack with ROM hacking? Love reverse-engineering the Pokémon games? Or perhaps you love your assembly language. This is the spot for polling and gathering your ideas, and then implementing them! Share your hypothesis, get ideas from others, and collaborate to create!
Research & Development programs in this forum are subject to moderator approval before they are displayed.


Reply
 
Thread Tools
  #1    
Old October 20th, 2013, 03:43 PM
Shiny Quagsire's Avatar
Shiny Quagsire
Being a Wizard
 
Join Date: May 2009
Location: Hoenn Safari Zone
Age: 16
Gender: Male
Nature: Jolly
So the other day I was on the RomHack.me IRC and I had the inspiration after finishing the Crystal Intro that it'd be cool to replace the title screen too. At the time it was just a small tease, a hypothetical idea, but a month later I had the desire to go and implement this idea, and this is what I created:



When I was first making this title screen I started by finding the callback associated with the title screen. Once I found it, I discovered that when replacing the existing pointer with the pointer to my custom intro, it would actually go to my intro upon triggering the intro to title screen change (which effectively made my intro loop forever at the time). Since it worked without problems, I got out my Intro Template and began making modifications to create a custom title screen.

A few notes about this hack:
  • Since it modifies the game engine code, it is not reliant on my custom intro, meaning that it plays nice with both the original intro as well as my custom intro.
  • It works seamlessly within the game and properly goes to the Continue, New Game menu
  • The Berry Update and Save Delete menus are properly implemented with the same shortcut keys as before, making it have no disadvantages when compared to the original title screen (while giving it several advantages)

Since I already detailed how I hacked the GameFreak intro using callbacks, I'm going to leave that out and start with how the title screen works.

Basically, our title screen runs in steps. We start by loading all of our resources and setting things up in this clippet of code:
Code:
if(init[27] == 0)
{
	initTitle();
	killSong();
	playSong(278);
	unfadeScreenWhite();
}
Here we use init[27] as a sort of step counter, which is increased in initTitle(). We then kill the previous song from the intro and start to play ours while unfading the screen.

Over the next few steps I modified BG0VOFF to bring in the Skitty and Wailord from the bottom of the screen where they were tucked away:
Code:
else if(init[27] == 2)
	{
		moveSkitty();
		if(VAR[4] == 1)
		{
			VAR[5] = VAR[5] + 3;
			VAR[4] = 0;
		}
		else
		{
			VAR[5] = VAR[5] + 2;
			VAR[4] = 1;
		}
		
		if(VAR[5] == 0 || VAR[5] > 0x1FF)
		{
			VAR[5] = 0;
			moveSkitty();
			init[27] = 3;
			TIMER[0] = 0;
		}
	}
The code here is a bit messy, but it's mostly due in part to the fact that we cannot use variables. To simplify, VAR[4] toggles on and off to increase VAR[5] by either 2 or 3. VAR[5] is then read and written to BG0VOFF to move the Skitty up from the screen. Once it has moved all the way (or too far) we reset it to 0 and begin the next steps to moving the logo, which is the same except it uses BG2Y since we're in mode 1.

Once everything is settled in we start spawning our OAMs into Game Freak's OAM system. All of the OAM info is located within the same file as the sprites that go with the OAM. Since the OAM system is quite complicated, I'll keep it a bit more simple. If you're interested in seeing how the OAM system works you can check out knizz's OAM-system Research Thread.

Code:
void spawnFlavor()
{
	int x = (240 / 2) - (64 / 2);
	int y = 70;
	int i = spawnOamFromUnk(&flavorTemplate,x,y,0x0);
	OAMBuffer[i].oam.tileProPal = 0;
	i = spawnOamFromUnk(&flavor2Template,x+64,y,0x0);
	OAMBuffer[i].oam.tileProPal = 0;
}
This code (located in titlescreen.c) spawns the OAMs for the "Hot Skitty on Wailord Edition". Each is a 64x32 OAM layed out to a 128x32 sprite. This function spawns each half in the center of the screen and changes it's palette to use palette 0.

After spawning the bottom text and flavor, we then begin the looping of our routine, making skitty's eyes glow while also spawning particles that float up the screen.
Code:
	else if(init[27] >= 6)
	{
		if(random(3) == 2)
			spawnParticle();
		moveUp();
		if(!isFading() && VAR[0] < 0x20)
			pulseEyes();
	}
In this code, we create a random condition that will spawn a particle on a 1/3 chance, which gives us a good amount of particles while also not flooding the screen. I should note that since GameFreak's system has a lot of extra data, only 64 out of the 128 possible OAMs are able to be used, which is quite limiting in some aspects.

In the moveUp() function we have the logic that will move our particles. One of the small details in the video that you may or may not have noticed is that there are actually 2 types of particles. The fast animating ones that scroll up quickly and the large dots that scroll up slower, giving it a 32-ish view of the particles. To do this, we assign each particle at random a type by not only setting it's animation data but also the derp1 property to either 1 or 0. Since this property is unused we can store data in it.
Code:
void moveUp()
{
	if(VAR[14] == 0xFFFF) //Make sure we don't start scrolling until all of our text and stuff is there.
		return;
	for(int i = VAR[14]; i < 0x3F; i++)
	{
		if(OAMBuffer[i].derp1 == 1)
			OAMBuffer[i].pos1.y -= 2;
		else
			OAMBuffer[i].pos1.y -= 1;
		if((int)OAMBuffer[i].pos1.y > 0x160 || (int)OAMBuffer[i].pos1.y < 0)
		{
			deleteOAM(i);
		}
	}
}
To make this system a bit more flexible, we assign VAR[14] to the last OAM from the text and previous OAMs, which gives us the range of OAMs we actually want to scroll. Faster particles move in increments of 2 while the others move in increments of 1. If our particle leaves the screen, we delete it.

Now the pulseEyes() method is a bit more complicated. To do this, we increase and decrease skitty's eye palette by 0x1 every 6 frames. In addition to this, we also pause for 20 frames after it goes completely dark or bright so that it isn't just pure oscillation.

Once we have reached the main loop, we start to allow key input. If the users press A or Start, we lock that input, play the cries, and fade out into the next menu. However, if the player presses B and Select at the same time, this code is executed:
Code:
else if(keyDown(KEY_SELECT) && keyDown(KEY_B))
	{
		if(keyDown(KEY_UP))
		{
			int (*func)(void) = (int (*)(void))0x080796CD; //Save Delete
			func();
		}
		else
		{
			int (*func)(void) = (int (*)(void))0x080796E9; //Berry Update
			func();
		}
		return;
	}
This basically allows us to keep the old functions from the previous title screen in case the game is loaded onto a flash cartridge and used to fix the RS berry glitch or they need to delete their save data. It was also cool to add in there.

Just as a last note, I think there's still some things that can be improved on with this system while also giving some key advantages. Since we have the entire code, it is possible to change anything with the title screen we want. However, this also means that it is prone to bugs and other oddities. For the full source code, you can download it and contribute back to it on my GitHub. If you have any questions, feel free to comment on this thread or PM me. And don't forget that since this is released under GPLv3 any hacks that use it are required to open-source any modifications done to it.
__________________



Reply With Quote
Reply
Quick Reply

Sponsored Links
Thread Tools

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
Minimum Characters Per Post: 25



All times are UTC -8. The time now is 06:51 AM.


Style by Nymphadora, artwork by Sa-Dui.
Like our Facebook Page Follow us on Twitter © 2002 - 2014 The PokéCommunity™, pokecommunity.com.
Pokémon characters and images belong to The Pokémon Company International and Nintendo. This website is in no way affiliated with or endorsed by Nintendo, Creatures, GAMEFREAK, The Pokémon Company or The Pokémon Company International. We just love Pokémon.
All forum styles, their images (unless noted otherwise) and site designs are © 2002 - 2014 The PokéCommunity / PokéCommunity.com.
PokéCommunity™ is a trademark of The PokéCommunity. All rights reserved. Sponsor advertisements do not imply our endorsement of that product or service. User generated content remains the property of its creator.