Pathfinder 2.0 System and how to install it (Now supports multiple unique path types!)

MistSonata

Moderator
-- What you need to know

First of all, you might want to have some familiarity with the hexadecimal system, as we're going to rely pretty heavily on it. There are plenty of resources for you out there, including Joe's own video that covers the subject. You will also need a text editing program. You can use notepad, but I highly suggest picking up Notepad++. You also might try to familiarize yourself with the path system, and there are a few videos on Joe's channel that will walk you through how they work, but I'm going to briefly cover it here too.

You'll also want to keep in mind that this version changes the way ALL paths work, it's possible in the future that I'll come up with a way to have multiple path systems in one game, but for now this is what you're working with.

Also, this tutorial is for the NESmaker Pi Beta, so if you have a version that's later than that, it might not work (unless specified otherwise). It's probably best to backup your files if you're worried about messing it up.

With all that out of the way, let's start cracking.

-- Installing the code

The first thing you need to do is to insert the Pathfinder code into a some of the .asm files in your NESmaker project folder. Specifically you'll want to find "MainASM.asm" (it's in the GameEngineData folder) and insert the following code near the very bottom right after the line ".include "GameData\HUD_DEFINES.dat"":
Code:
	.include "Routines\UserScripts\pathfinder.txt"

It should now look like this:
7sHSYtV.png


NOTE: You no longer need the "Pathfinder:" line above the .include, don't bother putting it in unless you really want to.

Then you need to find the SystemVariables.asm in "GameEngineData\Routines\Variables\" and replace the following variables...

45XeZc7.png


...with this code:
Code:
	Path_Neighbors				.dsb 1

After that it should look like this:

2kOCdKe.png


Then you're going to need to find LoadMetaNametableWithPaths.asm in "GameEngineData\Routines\System\" and replace everything from "handlePath:" to right before "GetSingleMetaTileValues:" with the following code.

Code:
handlePath:
	LDA #$00
	STA Path_Neighbors ;; This the variable that we're going to store the surrounding paths in.
	
	;; Bit 0 at the top center, and going clockwise around the tile from there.
	;; 1= Same Path in this slot, 0= Not the same path in this slot. Pretty simple, yo.
	
	;; +---+---+---+
	;; | 7 | 0 | 1 |
	;; +---+-+-+---+
	;; | 6 |-+-| 2 |
	;; +---+-+-+---+
	;; | 5 | 4 | 3 |
	;; +---+---+---+
	
	STY nt_index_hold ;; nt_index_hold is how we can now restore y if we have to 
				;; corrupt it.
		

	;; checkTopEdgePath:
	CPY #$10
	BNE checkLeftEdgePath
	LDA #%10000011
	STA Path_Neighbors ;; If the path is along the top row, set the top bits to 1 so that the path connects to the next screen

checkLeftEdgePath:
	TYA
	AND #%00001111
	BNE checkRightEdgePath
	
	LDA Path_Neighbors
	ORA #%11100000
	STA Path_Neighbors
	;CMP #%11100011
	;BEQ checkCRPathNeighbor ;; If both top and left sides are on the edge, there's no sense checking the bottom or right edges or re-checking the top row

checkRightEdgePath:
	INY
	TYA
	AND #%00001111
	BNE checkBottomEdgePath 
	
	LDA Path_Neighbors
	ORA #%00001110
	STA Path_Neighbors
	;CMP #%10001111
	;BEQ checkBCPathNeighbor ;; If both top and right sides are on the edge, there's no sense checking the bottom edge or the top and right tiles

checkBottomEdgePath:
	LDA nt_index_hold
	CMP #$E0
	BCC checkTLPathNeighbor
	LDA Path_Neighbors
	ORA #%00111000
	STA Path_Neighbors ;; Now that edges are taken care of, we can fill in the blanks

checkTLPathNeighbor: ;bit 7 - Checking this first so that we go from top to bottom (and make it easier to skip if top edge was set)
	LDA nt_index_hold 
	SEC
	SBC #$11 ; one row up, one value to the left.
	TAY
	LDA	(temp16),y
	CMP temp
	BNE checkTCPathNeighbor

	LDA Path_Neighbors
	ORA #%10000000
	STA Path_Neighbors
	
checkTCPathNeighbor: ;bit 0
	INY
	LDA (temp16),y ;; this is for top center.
	CMP temp
	BNE checkTRPathNeighbor
	
	LDA Path_Neighbors
	ORA #%00000001
	STA Path_Neighbors

checkTRPathNeighbor: ;bit 1
	INY
	LDA (temp16),y ;; this is for top center.
	CMP temp
	BNE checkCRPathNeighbor

	LDA Path_Neighbors
	ORA #%00000010
	STA Path_Neighbors
	
checkCRPathNeighbor: ;bit 2 - one of the edge checks skip to here
	LDY nt_index_hold
	INY
	LDA (temp16),y
	CMP temp
	BNE checkBRPathNeighbor

	LDA Path_Neighbors
	ORA #%00000100
	STA Path_Neighbors
	
checkBRPathNeighbor: ;bit 3
	TYA
	CLC
	ADC #$10 ;shift down one row
	TAY
	LDA (temp16),y
	CMP temp
	BNE checkBCPathNeighbor
	
	LDA Path_Neighbors
	ORA #%00001000
	STA Path_Neighbors
	
checkBCPathNeighbor: ;bit 4 - one of the edge checks skip to here
	AND #%01110000
	CMP #%01110000
	BEQ skipNeighborCheck ;This'll save a little time if it triggered the bottom edge check
	
	DEY
	LDA (temp16),y
	CMP temp
	BNE checkBLPathNeighbor
	
	LDA Path_Neighbors
	ORA #%00010000
	STA Path_Neighbors
	
checkBLPathNeighbor: ;bit 5
	DEY
	LDA (temp16),y
	CMP temp
	BNE checkCLPathNeighbor

	LDA Path_Neighbors
	ORA #%00100000
	STA Path_Neighbors
	
checkCLPathNeighbor: ;bit 6
	LDY nt_index_hold
	DEY
	LDA (temp16),y
	CMP temp
	BNE skipNeighborCheck

	LDA Path_Neighbors
	ORA #%01000000
	STA Path_Neighbors
	
skipNeighborCheck:

	LDA temp
	AND #%00001111 ;; get path type, EG $82 would be path 1, path type 3
	TAY
	LDA temp
	AND #%11110000 ;; get path number alone and store it back into temp
	STA temp
	

	LDA PathFinderlo,y
	STA pathtemp16
	LDA PathFinderhi,y
	STA pathtemp16+1

	LDA Path_Neighbors
	AND #%00000111
	TAY
	LDA (pathtemp16),y
	CLC
	ADC temp
	STA	updateTile_01
	
	
	LDA Path_Neighbors
	LSR A
	BCC leaveBit7alone
	ORA #%10000000
leaveBit7alone:
	LSR A
	STA Path_Neighbors
	AND #%00000111
	CLC
	ADC #$08
	TAY
	LDA (pathtemp16),y
	CLC
	ADC temp
	STA	updateTile_03

	LDA Path_Neighbors
	LSR A
	LSR A
	STA Path_Neighbors
	AND #%00000111
	CLC
	ADC #$10
	TAY
	LDA (pathtemp16),y
	CLC
	ADC temp
	STA	updateTile_02
	
	LDA Path_Neighbors
	LSR A
	LSR A
	AND #%00000111
	CLC
	ADC #$18
	TAY
	LDA (pathtemp16),y
	CLC
	ADC temp
	STA	updateTile_00
	
	LDY nt_index_hold

	RTS

Once you're done, it should look like this:

AxpeIBX.png

tn5Kk87.png


PF 2.0 UPDATE: The code for the "handlePath" subroutine has changed, make sure you copy in the new one. Also, you will also need to replace the "GetSingleMetaTileValues" subroutine with the following:

Code:
GetSingleMetaTileValues:
	STA temp
	CMP #$C0
	BCS notPath
	CMP #$80
	BCS doPathUpdate
	
	JMP notPath

Finally, once all that's done, create a new text file in "GameEngineData\Routines\UserScripts\", call it pathfinder.txt and insert the following text:
Code:
PathFinderlo:
.db #<PathFinder00, #<PathFinder01, #<PathFinder02, #<PathFinder03, #<PathFinder04, #<PathFinder05, #<PathFinder06, #<PathFinder07
.db #<PathFinder08, #<PathFinder09, #<PathFinder0A, #<PathFinder0B, #<PathFinder0C, #<PathFinder0D, #<PathFinder0E, #<PathFinder0F

PathFinderhi:
.db #>PathFinder00, #>PathFinder01, #>PathFinder02, #>PathFinder03, #>PathFinder04, #>PathFinder05, #>PathFinder06, #>PathFinder07
.db #>PathFinder08, #>PathFinder09, #>PathFinder0A, #>PathFinder0B, #>PathFinder0C, #>PathFinder0D, #>PathFinder0E, #>PathFinder0F

PathFinder00:
;; current path system
.db $04, $06, $04, $06, $03, $0d, $03, $00 ;; NE
.db $0a, $09, $0a, $09, $06, $0e, $06, $0f ;; SE
.db $07, $05, $07, $05, $08, $0c, $08, $00 ;; SW
.db $01, $02, $01, $02, $05, $0b, $05, $0f ;; NW

PathFinder01:
;; 32-tile path (uses all the path tiles on its row and also the row below it, not recommended for path #4)
.db $04, $16, $14, $1E, $03, $0d, $12, $0f ;; NE
.db $0a, $09, $1a, $18, $06, $0e, $1c, $1f ;; SE
.db $07, $05, $17, $1b, $08, $0c, $19, $10 ;; SW
.db $01, $02, $11, $13, $15, $0b, $1d, $00 ;; NW

PathFinder02:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder03:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder04:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder05:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder06:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder07:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder08:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder09:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder0A:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder0B:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder0C:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder0D:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder0E:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

PathFinder0F:
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

Make sure to save the text file after you're done, and you should be ready! Fire up NESmaker and import your favorite path tiles, then test them out on a screen to see.... that they look exactly the same as before...?
Yes, the pathfinder.txt code above instructs NESmaker to construct paths the same way it already does currently, but in the next post I'll show you how to customize it to behave the way YOU want it to.
 

MistSonata

Moderator
UPDATE: Before getting started on the next steps, there's one or two things you need to do to get the code working:

First you'll want to find the file ZP_and_vars.asm in GameEngineData\Routines\Variables\ and add the following line to the temp variables:

Code:
pathtemp16		.dsb 2

It should look like this:

d47a75596e514f0a3937a9796bf603ed.png


-- A Brief Summary of How Paths Work

Have you ever done one of those paint-by-numbers things? Well that's kind of how NESmaker draws the background, except all the painting areas are 16x16 pixel boxes in a grid. It reads the screen data top to bottom and left to right, looks up what tiles that hex value represents, and then places those tiles there.

But path tiles work a little differently than that, you can't just have it read "Path one is here, so just grab the 16x16 tile in that area of the PPU and slap it on the screen", you need to first be aware of the values of the boxes surrounding that path tile.

88Y9GKB.png


In the pathfinder code, what it does is it reads the 8 tiles surrounding it, and generates a 3 bit code to tell each 8x8 pixel quadrant of that tile (labelled NE, SE, SW, NW) what graphic it needs to pull from the PPU. This isn't REALLY necessary to fully understand in order to write your own pathfinder.txt, but it does help.

-- Making Your Own Pathfinder File

Now, before you start tinkering with the pathfinder.txt file, you'll want to make a copy of it just in case. In fact you might want to just make multiple copies for different path styles that you like so you can swap them in and out as you will (and maybe save them until I can figure out how make multiple path styles work).

Okay, so, all these hex values may seem daunting at first, but once you know what they represent, it'll probably be easier to comprehend.

Here's a blank pathfinder table just to start with...
Code:
;;PathFinder
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SE
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; SW
.db $00, $00, $00, $00, $00, $00, $00, $00 ;; NW

Now, here's what each of these hex values represent:
qFoTohj.png


You'll notice that each row represents one of the four quadrants in the path tile, starting with the northeast one. All the quadrants only pay attention to the neighboring tiles they're touching, and the columns show all the scenarios that each quadrant could experience (you'd think there'd be more than 8, but there's not, and that makes it very simple to manage). So the question you might be left with is... what do we put in these hex value places?

-- What Do We Put In These Hex Value Places??

Looking just at the NE quadrant, we can determine which situation we'll want to insert certain tiles into. Making the first tile of the path graphics your starting point (essentially $00) you can count up from there in hexidecimal, up to $0F.

dfdePzx.png


Since we're only working on a 16 tile path, we can set the 1st and 3rd hex value to $04, that's really all there is to it. Do that for the rest of the values and you're good to go!

Now, you might be asking how we get from here to using 32 unique tiles. The offsets are basically telling the program "Hey, this path's starting point is at $80, so add the offset to that to get $84 and grab the tile that's in that slot", so if you want it to go further than the current 16 tile path and into the next path, you need to add $10 to the number. So if you want the 4th tile from the path below the current one, you'd write $14 instead.

And this doesn't only work for going down, you can go up by wrapping the first digit around to F, so if you want the 4th tile in the path above the one you're working with, you would write $F4.

By the way, you can use this to go further up and use tiles from other places in the PPU:

UFyFUm3.png


Here I modified the pathfinder file I made to use a tile from the manticore (which is part of the background graphics, I didn't pull this from the sprite PPU). Now, doing stuff like this for fun is cool, but if you want to use this method regularly, you're going to have to do a LOT of planning ahead, since all the other parts of the background PPU can change from screen to screen. You've got to be careful there, or you'll end up with a garbled mess.

Anyway, that should be it. Let me know if you guys have questions, or need clarification on something.

Pathfinder 2.0 Update: As long as you've followed the instructions and made the changes highlighted above, the new pathfinder should work for you. However, if you want to make the most of the new system and all of its 16 path types, and you don't mind a whole lot of tedium, scroll down to my next post in the thread!
 

Rob Burrito

New member
Thanks for this explanation! starting to understand this system a bit more. i was curious if there was a call/command to flip a path tile, to double the available graphic tile space? (similar to the ability to flip game object and monster tiles)
 

MistSonata

Moderator
Rob Burrito said:
Thanks for this explanation! starting to understand this system a bit more. i was curious if there was a call/command to flip a path tile, to double the available graphic tile space? (similar to the ability to flip game object and monster tiles)

Unfortunately, no. You can't flip background tiles, only sprite tiles.
 

MistSonata

Moderator
-- Using Pathfinder 2.0

Okay, so here's where it gets complicated. Right now this is kind of a backdoor hack to make path types work until there's an easier way to do it with NESmaker. When NESmaker draws paths, it's using the hex values $80, $90, $A0, and $B0 to represent them in the nametable data, but in order to define which path type we want a path to use, we will need to change the zeroes on these values into the path type number that we want to use. ($81 for path number 1 ($80) and path type 1 (Pathfinder01) etc)

There's just one problem with that, though. Even if you go into "GameEngineData\ScreenData\Nametables\" and edit the nametable file for the screen you want to define your path types on, as soon as you export and test your game in NESmaker, the tool will override your hard earned work.

What you'll have to do is find the nametable file for the screen you want to customize and copy it, then move that copy to a new folder (for example, I chose to put them in "GameEngineData\ScreenData\UserNametables\", to make it easier.

After you've done that, you'll need to go into your Bank00.asm file in "GameEngineData\BankData\", find the .include for the nametable you copied, and modify it to include the custom "UserNametable" file instead.

30b9c925b0c7c77b7ea41cf2402dd21d.png


I'd highly recommend doing this after you're done editing the screen in NESmaker, and just want to define your path types. If you only have one path type in mind for each path, it shouldn't be too difficult at that point to perform a "find and replace" to make your life a little easier.

Oh, and here's a video demonstrating the new pathing system in action! Hope you guys find this stuff useful!

Happy trails, everyone! :D
 

MistSonata

Moderator
Just a quick little update. I recently tested the pathfinder system on NESmaker 4.0.11, and it seems to work without issue!
 
Top Bottom