[4.5.9] Moving Nametable to Nametable/ Screen to Screen from Exit Tiles

CatfortSoftware

New member
Found a way to move screen to screen, and I thought I'd share what I was able to get together since I don't see it around here anywhere.

The goal of this system is to create a tile collision type that can be placed at the edge of the screen that will send the player to the next screen over, akin to LoZ.

If you don't have a spot for them already, create a new asm file for custom tables (ie CustomTables.asm) and place it in the subroutines folder. then, open up the LoadAllSubroutines.asm from the same folder, and include your custom tables file under the include for ToggleTables.asm.

Code:
...   
    .include ROOT\Game\Subroutines\doCleanUpSpriteRam.asm
    .include ROOT\Game\Subroutines\doWaitFrame.asm
    .include ROOT\Game\Subroutines\doHandleBounds.asm
    .include ROOT\Game\Subroutines\doLoadScreen.asm
    .include ROOT\Game\Subroutines\doLoadScreen2.asm

    .include ROOT\Game\Subroutines\ToggleTables.asm
    .include ROOT\Game\Subroutines\CustomTables.asm         ;custom table goes here
    .include ROOT\Game\Subroutines\ejectionSubroutines.asm
    .include ROOT\Game\Subroutines\doLoadScreenData.asm
...


add three tables to the file, as shown below.

Code:
ScreenChangeEdges: ;tile positions that allow screen changes (from down, up, left, right), adjust as needed for HUD placement

.db #$B0, #$30, #$00, #$F0 ;I'm using #$B0 and #$30 for my vertical exit tiles, since I have a 3 tile HUD and a 3 tile black bar at the bottom of the screen

ScreenChangeWarpValues: ;tile offsets for screen entry (from down, up, left, right), adjust as needed for sprite size and HUD

.db #$30, #$B0, #$E8, #$00 ;since my character is 3 sprites wide, I need to adjust my right side entry a half tile to not wrap around

ScreenChangeDirectionMasks: ;0000DULR, used to check against the players' current facing direction

.db #%00001000, #%00000100, #%00000010, #%00000001

ScreenChangeFacingTable: ;in the facing direction order, converts facing direction into a 0000DULR format

.db #%00001000, #%00001001, #%00000001, #%00000101, #%00000100, #%00000110, #%00000010, #%00001010


next, in your project settings, we'll need to add two new variables to the OverflowRAM- _nextScreenDirection, and _nextScreenHoldPosition, 1 byte each

1658182403570.png

Next, create a new tile script called ChangeScreenTile.asm. in your projects settings, assign ChangeScreenTile.asm to one of the tile collision scripts.
open it up, and add the following code. the code works as follows:

  1. if collision is with the player,
  2. initialize Y and _nextScreenDirection to 0, and store off currentNametable. we'll increment Y for each direction check, going 0-3 for down, up, left, and right respectively.
  3. store off xHold_hi into _nextScreenHoldPosition and check the collided tile's y position against the value in ScreenChangeEdges at Y.
  4. if the tile is at the bottom or top of the screen, add or subtract 1 respectively from the current nametable's left nibble, and jump to the end of the direction check.
  5. else, store off yHold_hi into _nextScreenHoldPosition, and check the collided tile's x position against the value in ScreenChangeEdges at Y.
  6. if the tile is at the right or left of the screen, add or subtract 1 respectively from the current nametable's right nibble, and jump to the end of the direction check.
  7. if we found a direction, store Y off into _nextScreenDirection. if not, jump to done.
  8. get the player's facing direction, get it's adjusted direction-per-bit value from ScreenChangeFacingTable, and compare it to the ScreenChangeDirectionMasks at the index of _nextScreenDirection.
  9. AND the values together. if the result is 0, jump to done.
  10. else, we had a succesful warp! load our current Map from warpMap into warpToMap, and store the adjusted nametable value into warpToScreen. then, call the WarpToScreen macro, with #$03 as our transition type

Code:
    CPX player1_object
    BEQ +isPlayer
    
        JMP +done
    
    +isPlayer
        
    LDA #$00
    STA _nextScreenDirection
    
    ;compare tile location to defined edges.
    ;this can probably be compressed down into a loop with
    ;some parlor trickery, but this will do the job
    
    ;check for vertical exit, maintaining the X position
    LDA xHold_hi
    STA _nextScreenHoldPosition
    
    LDA currentNametable
    STA temp
    
    LDY #$00
    LDA tileY
    AND #$F0
    CMP ScreenChangeEdges,y
    BCC +notDown   
    
        LDA temp
        CLC
        ADC #$10
        STA temp   
        JMP +foundNextScreenDirection
    +notDown
    
    INY
    LDA tileY
    AND #$F0
    CMP ScreenChangeEdges,y
    BEQ +up
    BCS +notUp
    +up       
        LDA temp
        SEC
        SBC #$10
        STA temp
        JMP +foundNextScreenDirection
    +notUp
            
            
    ;check for horizontal exit, maintaining the Y position
    LDA yHold_hi
    STA _nextScreenHoldPosition
            
    INY
    LDA tileX
    AND #$F0
    CMP ScreenChangeEdges,y
    BEQ +left
    BCS +notLeft
    +left
        LDA temp
        SEC
        SBC #$01
        STA temp
        JMP +foundNextScreenDirection
    +notLeft
    
    INY
    LDA tileX
    AND #$F0
    CMP ScreenChangeEdges,y
    BCC +notRight
    
        LDA temp
        CLC
        ADC #$01
        STA temp
        JMP +foundNextScreenDirection
    +notRight
        JMP +done

    +foundNextScreenDirection
        
    STY _nextScreenDirection
    ;check if we're facing the correct direction - will give is the ability to warp closer to the edge on the next screen
    ;and not trigger the screen change tiles
    
    LDA Object_direction, x
    AND #%00000111
    TAY
    LDA ScreenChangeFacingTable, y
    STA temp1
    LDY _nextScreenDirection
    LDA ScreenChangeDirectionMasks, y
    STA temp2
    
    LDA temp1
    AND temp2
    BEQ +done

        ;succesful warp!
        
        LDA warpMap
        STA warpToMap
    
        LDA temp
        STA warpToScreen
        
        WarpToScreen warpToMap, warpToScreen, #$03
        ;; arg0 = warp to map.  0= map1.  1= map2.
        ;; arg1 = screen to warp to.
        ;; arg2 = screen transition type - most likely use 1 here.
            ;; 1 = warp, where it observes the warp in position for the player.
+done

Our last goal is to set up loading a screen by this method. When calling the WarpToScreen Macro, the third argument is usually used to denote warp type, and is processed in doLoadScreenData.asm.
#$00 skips the whole thing, #$01 uses the position stored in the screen info, and #$02 is used for if you do a continue. This leaves us the ability to expand on what we can do by using up #$03 for moving screen to screen.

in doLoadScreenData.asm, look for the Warp In Position code (as of writing, this will be around line 99 in 4.5.9). replace it with the following. this code adds a branch for a screen transition type of #$03, and loads up the position we want to place the character at from the data we stored off. if it was a vertical change, we load up the Y position from the ScreenChangeWarpValues table, and the X position from the value we stored off from the collision. vice versa for horizontal changes.

Code:
...
;; 128 - Warp In Position
;; This might need to be skipped under certain warp conditions
;; and should only be observed if the transition is of warp type.
    LDA screenTransitionType
    BEQ skipSettingWarpInXY
    CMP #$01
    BEQ setToStartValue
    CMP #$02
    BEQ setToContinueValue
    CMP #$03                ;here we add the branch to screen changes
    BEQ setToNextScreenValue
        ;;; other cases go here
setToContinueValue
    LDA continueX
    STA newX
    LDA continueY
    STA newY
    JMP skipSettingWarpInXY
setToStartValue:

    LDA (collisionPointer),y
    AND #%11110000
    STA newY
    
    LDA (collisionPointer),y
    AND #%00001111
    ASL
    ASL
    ASL
    ASL
    STA newX
    JMP skipSettingWarpInXY
    
setToNextScreenValue: ;here we get our stored values from our screen change and load the position

    TYA
    PHA
    
        LDY _nextScreenDirection
        LDA ScreenChangeWarpValues, y
        STA temp
    
    PLA
    TAY

    LDA _nextScreenDirection
    CMP #$02
    BCS +notVertical
    
        ;is vertical
        LDA temp
        STA newY
        LDA _nextScreenHoldPosition
        STA newX
        JMP +doneWithScreenShift
    
    +notVertical

        ;is horizontal
        LDA temp
        STA newX
        LDA _nextScreenHoldPosition
        STA newY
        
    +doneWithScreenShift

    LDA #$00
    STA _nextScreenDirection

skipSettingWarpInXY:
    LDA #$00
    STA screenTransitionType ;; reset screen transition type
...

lastly, in your nametable, add some screen change collision at the edges. I have them as tile 05, as seen in the reference image below:
1658186737164.png

if the player collides with the tiles on the left side of the screen, they will move one name table to the left. if the player collides with the tiles on the right side of the screen, they will move one nametable to the right. when I have vertical exits, I place them at the tile above and below the black bar and hud, and have those tile locations defined as my vertical edges in ScreenChangeEdges.
the player will appear on the next screen at the edge on the opposite side, with the opposite value being determined in ScreenChangeWarpValues. since my character is 3 sprites wide, for instance, I'll need to make a little extra buffer space to ensure that my sprite doesn't wrap around when I enter from the right side of the screen.

and there you have it! that should get you a pretty good screen to screen movement. the only small bug I have right now with it is that you sometimes cannot move in certain directions at the entry point, but it's not currently a hinderance to overall gameplay. But this should be a good start!

ScreensChangeTest.gif
 

SciNEStist

Well-known member
I'm not sure I understand this. Can you please explain the benefit of using this method instead of setting the "edge object reaction" in the player object details to "Go to next screen"?
 
I'm not sure I understand this. Can you please explain the benefit of using this method instead of setting the "edge object reaction" in the player object details to "Go to next screen"?
same here, was hoping it could be like the original LOZ like you said, but it's only half like that? not trying to offend @CatfortSoftware but is this specific script still a WIP? regardless if you can figure that out or not I was going to try that for future NES games... don't count on that feature though like YEARS from now
 

CatfortSoftware

New member
... huh! well, I was not aware that was an option, which I suppose is why I thought there was a benefit!
thanks for pointing that out. @Lord_Klump_Of_Kongo_Bongo, no offense here- it's definitely a WIP for the most part. while full LoZ screen shifting was not the goal, I think it'd be cool to try and figure out. Once I figure out scrolling/swapping the mirroring on the fly, I think I'll give that a little RND
 

SciNEStist

Well-known member
I can see a use for these tiles if you make them so you can position them anywhere on the screen, for times when you want the walkable space to not fill the whole screen ( such as doors inside dungeons or buildings.
 
Top Bottom