I've been studying the path system for a little while, trying to understand how it works, and looking at the LoadMetaNametableWithPaths.asm file, it's actually kind of hard to comprehend. It's not laid out the way I thought it would be (or the way I thought it would in my mind), so I thought I'd sort of lay out how I'd write them if I were more experienced with 6502.
So, what the current handlePath script does is check the 8 metatiles around it to see if they match the path tile it's comparing to, and if it does match with the current path (stored in temp) it writes that into one of 8 variables prepared specifically (I think?) for the handlePath code.
My version would use one dedicated variable, and would store whether the neighboring tiles are paths (or should be considered paths, in the case of screen edges or "ignore path" tiles) and store them as bits, starting with bit 0 at the top center and going around clockwise from there.
I've found that, for each quadrant (NE, SE, SW, NW), they really only need to read 3 of those 8 bits. So once you've set all the bits, you can focus on one quadrant and only read the 3 bits it needs and determine which tile it should load.
Now, as I plotted this out I noticed that the current path system has it so that each quadrant has 5 possible states that are split up between 8 different scenarios (shown above). And while I was thinking of how you would program it so that it reads both 000 and 010 as "exterior corner piece", I thought "why not use all 8 scenarios? It'd probably be easier".
So what if, for each path, we had NESmaker generate a table like this:
Code: Select all
+---+---+---+---+---+---+---+---+---+ | |000|001|010|011|100|101|110|111| +---+---+---+---+---+---+---+---+---+ |NE:| | | | | | | | | +---+---+---+---+---+---+---+---+---+ |SE:| | | | | | | | | +---+---+---+---+---+---+---+---+---+ |SW:| | | | | | | | | +---+---+---+---+---+---+---+---+---+ |NW:| | | | | | | | | +---+---+---+---+---+---+---+---+---+
This means that you could make a path with 32 unique tiles, but if you want to make it fewer than that, you can. You can make it behave exactly like the current paths do now, or you could make it so that you have two unique tiles for the left and top edges, but have the bottom and right edges use the same tile. You could have it so that instead of using an exterior corner piece in situation 010, you could have it connect diagonally to the path (the scenario for the SW quadrant in the path diagonal would also be 010). You could have it so that instead of using a straight edge in scenarios 110 and 011 you could have it start curving toward the direction the path is going. You could (I think?) have paths share tiles with each other while still behaving as though they're separate paths. You could even have all the offsets on the table point to the same tile so that it's just repeating the same tile over and over (not sure why you would, but you could is the point I'm making).
So I started trying to replicate the handlePath code, but do it so that it used the method described above.
Code: Select all
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 | ;; +---+---+---+ TYA STA 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: TYA AND #%00001111 BNE checkBottomEdgePath ;; (btw I'm only assuming this and the left edge code checks the edges, I actually have no idea how this works) 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) ;; Check the Top Left (bit 7) metatile for path. If it's the same, set that bit to 1. LDA Path_Neighbors ORA #%10000000 STA Path_Neighbors checkTCPathNeighbor: ;bit 0 ;; Check the Top Center (bit 0) metatile for path. If it's the same, set that bit to 1. BNE checkTRPathNeighbor LDA Path_Neighbors ORA #%00000001 STA Path_Neighbors checkTRPathNeighbor: ;bit 1 ;; Check the Top Right (bit 1) metatile for path. If it's the same, set that bit to 1. LDA Path_Neighbors ORA #%00000010 STA Path_Neighbors checkCRPathNeighbor: ;bit 2 ;; Check the Center Right (bit 2) metatile for path. If it's the same, set that bit to 1. LDA Path_Neighbors ORA #%00000100 STA Path_Neighbors checkBRPathNeighbor: ;bit 3 ;; Check the Bottom Right (bit 3) metatile for path. If it's the same, set that bit to 1. LDA Path_Neighbors ORA #%00001000 STA Path_Neighbors AND #%01110000 CMP #%01110000 BEQ skipNeighborCheck ;This'll save a little time if it triggered the bottom edge check checkBCPathNeighbor: ;bit 4 ;; Check the Bottom Center (bit 4) metatile for path. If it's the same, set that bit to 1. LDA Path_Neighbors ORA #%00010000 STA Path_Neighbors checkBLPathNeighbor: ;bit 5 ;; Check the Bottom Left(bit 5 metatile for path. If it's the same, set that bit to 1. LDA Path_Neighbors ORA #%00100000 STA Path_Neighbors checkCLPathNeighbor: ;bit 6 ;; Check the Center Left (bit 6) metatile for path. If it's the same, set that bit to 1. LDA Path_Neighbors ORA #%01000000 STA Path_Neighbors skipNeighborCheck: ;; ======= Once we have our Path_Neighbors bits loaded, we can get the values for each quadrant and grab their offsets from a table ======= ;; First we need to grab bits 0, 1, and 2 for the NE quadrant for the table offset (0-7) and then load the chr offset stored there into that quadrant. ;; LSR once, and if the carry is set, write 1 into bit 7 (this is important, as the NW quadrant needs the original value in bit 0) then LSR again, then do the same as above for the SE quadrant ;; LSR twice and record bits 0, 1, and 2 for the SW quadrant ;; Do the same as above for the NW quadrant (the original bit 0 should now be in bit 2) ;; You should now have all your quadrants set! :D ;; End handlePath
TL:DR - If my idea works, paths could be much more customizable with up to 32 unique tiles per path (the amount of tiles dedicated to paths would still be limited), opening up a wide range of versatility and the possibility for creative applications!