• 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?".
  • 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: [FR] Initiating and Manipulating the Overworld and Camera from C

Shiny Quagsire

I'm Still Alive, Elsewhere
697
Posts
14
Years
Greets once again,

If case the thread title was too confusing, basically what this does is it will initiate the overworld from any code point in the game (ie an intro sequence or the likes) and allow you to manipulate the camera. An example of a good use for this would be something like the flying overworld camera as seen in the credits sequence for Fire Red. That is basically what this is. Combined with the proper overworld loading functions and a few small RAM edits, I created a small C file which can be included and used for various things. I actually originally made it due to me being on GoGo's IRC and him mentioning that he would have needed something like this, and luckily I happened to be bored and curious enough to actually do it. That being said, the implementation:

Code:
#define mapMem				((u8 *)(*(u32*)0x03005008))
#define saveX				(*(u16 *)(*(u32*)0x03005008))
#define saveY				(*(u16 *)((*(u32*)0x03005008)+2))
#define mem 				(*(u32*)0x020370FC)		
#define mapLoadingState     ((u8 *)0x03003528)

#define mapAx				(*(u16*)(mem))
#define mapAy				(*(u16*)(mem+2))

#define mapxVel				(*(u32*)(mem+8))
#define mapyVel				(*(u32*)(mem+12))

#define mapX 				(*(u16*)(*(u32*)0x03005008))
#define mapY 				(*(u16*)((*(u32*)0x03005008)+2))

#define playerNPCID			(*(u8*)0x0203707D)

void initMap(u8 bank, u8 map, u8 x, u8 y)
{
	//if(mem != 0)
		//free(mem);
	mem = 0x020D0000;//Overriding here because malloc apparently can't actually give me a non-allocated area .-. //malloc(0x2000);
	(*(u8*)0x0203ADFA) = 3; //Disables map sign thingies on entry. Also gives some nice letterboxing bars if set to 2
	flag_set_(0x4001); //Disable map music change on map change
	(*(u32*)0x03005020) = 0x08059B8F; //Disable initial map music

	mapMem[4] = bank;
	mapMem[5] = map;
	saveX = x;
	saveY = y;

	doMapLoad();
	(*(u32*)0x03005050) = 0;
	(*(u16*)0x03000FC0) = 327; //Map music
}

void setMapXVel(u32 vel)
{
	mapxVel = vel;
}

void setMapYVel(u32 vel)
{
	mapyVel = vel;
}

void doMapLoad()
{
	mapLoadingState[0] = 0;
	int (*mapLoadLoop)(u32) = (int (*)(void))0x08056E5C+1;
	mapLoadLoop(0x03003528);
}

void updateMapRenderer()
{
	moveCam();
	doTheThing();
}

void fullRerender()
{
	int (*drawMap)(void) = (int (*)(void))0x0805A684+1;
	drawMap();
}


void moveCam()
{
	int (*updateCam)(void) = (int (*)(void))0x0805ABB0+1;

	mapAx = mapAx + mapxVel*2;
	mapAy = mapAy + mapyVel*2;
	(*(u32*)0x03005058) = mapxVel;
	(*(u32*)0x0300505C) = mapyVel;

	updateCam();

	init[0] = init[0] + 1;
}

void doTheThing()
{
	coro_del_(coro_getid_(0x0806E83C+1)); //Prevents random wild cries
	npc_hide_(0x02036E38+(((playerNPCID << 3) + playerNPCID) << 2));
	(*(u32*)0x03005020) = 0x08059B8F; //Disable initial map music
	int (*func2)(void) = (int (*)(void))0x0805ABB0+1;
	int (*func3)(void) = (int (*)(void))0x08115798+1;
	int (*adjustOverworlds)(void) = (int (*)(void))0x0805AE28+1;
	int (*func7)(void) = (int (*)(void))0x0806FFBC+1;
	int (*func8)(void) = (int (*)(void))0x080F67B8+1;
	int (*updateAnimsandOWs)(void) = (int (*)(void))0x08056A04+1;

		int (*copyPalToDMA)(void) = (int (*)(void))0x08070474+1;
		copyPalToDMA();

	func2();
	func3();
	adjustOverworlds();
	func7();
	func8();
	updateAnimsandOWs();
}

void coro_del_(int *addr)
{
	int (*func)(u32) = (int (*)(void))0x08077508+1;
	func(addr);
}

int coro_getid_(int *addr)
{
	int (*func)(u32) = (int (*)(void))0x08077688+1;
	return func(addr);
}

int npc_hide_(int *addr)
{
	int (*func)(u32) = (int (*)(void))0x0805E4B4+1;
	return func(addr);
}

void flag_set_(int *flag)
{
	int (*func)(u32) = (int (*)(void))0x0806E680+1;
	return func(flag);
}

The basic usage involves 4 functions: initMap, updateMapRenderer, setMapXVel and setMapYVel. To demo this I made a small C file based on one of my older intros, and later stripped of the unneeded functions. The basic usage, however, is done as such. One time on load, call initMap. On every frame in which you want to update the overworld, call updateMapRenderer. To adjust the camera, use setMapXVel and setMapYVel. In my case, I just bound the d-pad to create a sort of floating map viewer:


In the video you'll notice a few things. First, tile animations and overworlds work and function as proper. However they'll sometimes disappear off the camera due to some odd offsetting of the camera on occasion which occurs mostly when the velocities are not done at a proper pace like walking or biking (in this case it happened to be me rapidly pulsating left and right to the song). This also causes some issues with the scrolling as seen on the edges of the screen on occasion, and at the moment there isn't really a fix for this since it's purely an issue if how the camera was implemented in this example and in the engine. Luckily however this is a non-issue if you are properly manipulating the camera in code at a constant velocity.

As usual, the full source for this demo can be found at my Github (link below). I'm also including a patch for this demo in the event that anyone wants to explore or whatever (routine patches over the 0x790000 area). Controls are fairly simple. D-pad to move, B to redraw the map (if it gets weird looking). A and Start go to the titlescreen.
https://github.com/shinyquagsire23/FR-Overworld
 
43
Posts
9
Years
Although I don't have a use for this personally, I'm pretty sure that others can find a use for this, mainly for Machinama, or their ROM-Hacks. Still, neat to see though :P.
 

Deokishisu

Mr. Magius
990
Posts
18
Years
If you could force the screen to scroll at a certain speed and change directions at pre-determined points, you could also do some sort of scrolling mosaic/painting/mural/whatever to do some backstory on your region or the mascot of your hack or whatever by making that a map.

My thought was that it would be similar to how the backstory is explained in the second Monster Rancher game for the original PS1, it was basically an animated scrolling mural that explained how monsters came about.
 
Last edited:

Elaitenstile

I am legend
1,908
Posts
11
Years
  • Age 24
  • Seen Feb 27, 2015
Wow, this is something. I believe this is going to be extremely useful for credits, cutscenes and the likes. Now, if we could load pictures directly with their tilemaps onto the OW with a script... that would be something. Just saying, this may be the start of something extraordinary here.
 

Shiny Quagsire

I'm Still Alive, Elsewhere
697
Posts
14
Years
Wow, this is something. I believe this is going to be extremely useful for credits, cutscenes and the likes. Now, if we could load pictures directly with their tilemaps onto the OW with a script... that would be something. Just saying, this may be the start of something extraordinary here.

Definitely possible, BG0 is completely free along with the usual text palettes (although they get loaded along with the map, but they can be overwritten safely as long as you don't plan on using textboxes).
 
1
Posts
9
Years
  • Age 30
  • Seen Jul 21, 2019
This is awesome. I have been looking for a way to get something like this done, but in a cut-scene with text boxes and pre determined sprite events, and it replaces the professor oak's intro sequence.
The game starts normally after the cut-scene event. The cut-scene event however only plays out when you start a new game. Is there already a way to do this? I dont think its impossible.
 

GoGoJJTech

(☞゚ヮ゚)☞ http://GoGoJJTech.com ☜(゚ヮ゚☜)
2,475
Posts
11
Years
Didn't realize you posted this lol
Remember to have an uninitMap for C projects, which should be:

void uninitMap()
{
(*(u32*)0x030030FC) = 0x080EC5A4+1;
}
 
2
Posts
9
Years
  • Age 29
  • Seen Jun 30, 2015
Will this work for emerald also how do you put it in the ROM
 
Back
Top