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"":
It should now look like this:
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...
...with this code:
After that it should look like this:
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.
Once you're done, it should look like this:
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:
Finally, once all that's done, create a new text file in "GameEngineData\Routines\UserScripts\", call it pathfinder.txt and insert the following text:
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.
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:
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...
...with this code:
Code:
Path_Neighbors .dsb 1
After that it should look like this:
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:
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.