Help With bounds Handler glitch during scrolling?

Raftronaut

Member
Posting this issue here in case someone may have the ability to help.

This is regarding what I believe to be a bug in the nesmaker 4.1.5 scrolling core documented here: http://nesmakers.com/viewtopic.php?f=48&t=2481

I've been trying to determine why the player will wrap into the screen edge when presented with tile collisions during scrolling. Monster collisions do not seem to cause the player to do this at all. Followed the above thread to it's conclusions with all sorts of odd behavior ensuing when touching the screen edge as seen here: https://youtu.be/M09kcq8UO0c

I have no idea where to look for solutions at this point. I have been studying asm slowly but this problem is seemingly way above my comprehension. Any tips/pointers or general troubleshooting advice would be greatly appreciated.

I believe this is significant hindrance to anyone hoping to use the Nesmaker shooter module at this stage, I hope to document a correction for the community if at all possible.
 

Raftronaut

Member
So far I'd followed the directions in the linked thread. (Though I am not seeing my link working). Per Mugi's instructions, I deleted the macro CheckPlayerCameraPosition.asm and replace with the two separate files for CheckPlayerCameraPositionHorizontal.asm and CheckPlayerCameraPositionVertical.asm seen here:

CheckPlayerCameraPositionHorizontal.asm:
Code:
MACRO CheckPlayerCameraPositionHorizontal
	CPX player1_object
    BEQ notdoneWithCameraCheckHorizontal
	JMP doneWithCameraCheckHorizontal
notdoneWithCameraCheckHorizontal:
	LDA gameHandler
	AND #%10000000
	BNE gameIsActive
	JMP doneWithCameraCheckHorizontal
gameIsActive:
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; let's check to see if we're against the leftmost edge.
	LDA xScroll_hi
	BNE + ; not on screen 0
	;; is on screen zero
	LDA Object_h_speed_lo,x
	CLC
	ADC #$00
	LDA Object_h_speed_hi,x
	ADC #$00
	BPL + ;; not moving left.
	LDA Object_scroll,x
	BNE +
	LDA xScroll
	CMP xHold_hi
	BCS leftOfCameraForPlayer
	JMP checkRightEdge
+
    LDA nt_hold
    CMP xScroll_hi
    BCC leftOfCameraForPlayer
	;BEQ leftOfCameraForPlayer
    BNE checkRightEdge
    LDA xHold_hi
    CMP xScroll
    BCC leftOfCameraForPlayer
	;BEQ leftOfCameraForPlayer
    JMP checkRightEdge
leftOfCameraForPlayer:
    ;;; dont update position
    JMP doScrollingLeftBounds
checkRightEdge:
    LDA xScroll_hi
    CLC
    ADC #$01
    STA temp
    
    LDA xScroll
    SEC
    SBC Object_right,x
    STA temp1
    LDA temp
    SBC #$00
    STA temp

    CMP nt_hold
    BCC rightOfCameraEdgeForPlayer
	;BEQ rightOfCameraEdgeForPlayer
    BNE doneWithCameraCheckHorizontal
    LDA temp1
    CMP xHold_hi
    BCC rightOfCameraEdgeForPlayer
	;BEQ rightOfCameraEdgeForPlayer
    JMP doneWithCameraCheckHorizontal
rightOfCameraEdgeForPlayer:
    JMP doScrollingRightBounds
  
doScrollingLeftBounds:
    ;;; enter what should happen at bounds for player.
	;;; an RTS here will result in skipping position update.
	JSR doLeftBounds_player
    RTS    
    
doScrollingRightBounds:
    ;;; enter what should happen at bounds for player.
	;;; an RTS here will result in skipping position update.
	JSR doRightBounds_player
    RTS

doneWithCameraCheckHorizontal:
    ENDM


and CheckPlayerCameraPositionVertical.asm

Code:
MACRO CheckPlayerCameraPositionVertical
	CPX player1_object
    BEQ notdoneWithVerticalCameraCheck
	JMP doneWithVerticalCameraCheck
notdoneWithVerticalCameraCheck:
	LDA gameHandler
	AND #%10000000
	BNE gameIsActiveCheckVertical
	JMP doneWithVerticalCameraCheck
gameIsActiveCheckVertical:

;;;;; TOP CHECK
	LDA Object_y_hi,x
	CLC
	ADC #$7F
	STA temp
	LDA Object_y_lo,x
	CLC
	ADC Object_v_speed_lo,x
	LDA temp
	ADC Object_v_speed_hi,x
	BVC notAtBoundsTop
	LDA #$00
	STA xScroll
	JSR doTopBounds_player
	RTS
;;;;; BOTTOM CHECK
notAtBoundsTop:
	LDA Object_y_lo,x
	CLC
	ADC Object_v_speed_lo,x
	LDA Object_y_hi,x
	ADC Object_v_speed_hi,x
	ADC Object_bottom,x
	CMP #BOUNDS_BOTTOM
	BCS atBottomBounds
	JMP doneWithVerticalCameraCheck
atBottomBounds:
	LDA #$00
	STA xScroll
	JSR doBottomBounds_player
	RTS

doneWithVerticalCameraCheck:
    ENDM

Then updated the TileColision_4_1_0 tot he following:
Code:
HandleTileCollision:
    ;;;;;ASSUMES vulnerability bit 000x0000, if one, skips collision.
    ;;;;;Six collision points. 
    ;;;;;All collision points checked and registered for all objects.
    ;;;; a solid in any of the points will result in negating all others.
    
        ;;; There are 6 ram variables, collisionPoint0 - collisionPoint5.
        ;;; collisionPoint0 = top left
        ;;; collisionPoint1 = top right
        ;;; collisionPoint2 = bottom right
        ;;; collisionPoint3 = bottom left.
        ;;; collisionPoint4 = mid left
        ;;; collisionPoint5 = mid right.
    
    TXA 
    STA currentObject
    TYA
    STA tempy

	LDA npc_collision
	AND #%11111101
	LDA #$00
	STA npc_collision
	LDA navFlag
	AND #%11111110
	STA navFlag
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; EXPLANATION:
;; In this basic module, both non gravity and gravity scripts are loaded.
;; Under most circumstances, a user will likely make use of one or the other,
;; as they each take up a good chunk of data.  But the benefit of including both is that
;; a platforming game with gravity is just a simple bit flip in the screen flags.
    
    
    LDA screenFlags
    AND #%00100000
    BNE useGravity
    JMP dontUseGravity
useGravity:
	LDA Object_vulnerability,x
	AND #%00100000
	BEQ useGravity2
	LDA Object_physics_byte,x
	AND #%00000010
	BNE useGravity2
	JMP dontUseGravity
useGravity2:

;;;;;;===================================================
;;;;;;***************************************************
;;;;;; TILE COLLISION FOR SCROLLING PLATFORMERS:
;;;;;; For scrolling platformers we will use three types of collision detection:
;;;;;;          1. A routine to check under our feet to determine
;;;;;;              whether or not you are standing on a solid object.

                    ;;; This function will check under neath the object's feet
                    ;;; by arg0 pixels.
                    ;;; it will update bit 0 of the Object_physics_byte.
                    ;;; If that bit is 0, it means the place is free.
                    ;;; If that bit is 1, it means the place is solid.
    ;**********************
       CheckUnderObject #$01

;;;;;;
;;;;;;          2. A routine that checks to see if the potential position leaves you
;;;;;;              with your 'bottom' or 'top' in a solid object, in which case it ejects pixel perfect
;;;;;;              to that tile.

                    ;;; This function checks the POTENTIAL vertical position.
                    ;;; If it results in a point in a solid object, it appropriately
                    ;;; ejects and places the object directly against the point
                    ;;; of collision.
    ; **********************
     CheckForVerticalEjection 

  
;;;;;; Better check for any vertical updates now, or else
;;;;;; a horizontal collision could cancel out the required
;;;;;; bounds update
    CheckPlayerCameraPositionVertical


;;;;;;
;;;;;;          3. A routine which checks horizontal motion to see if the potential position
;;;;;;              sees a solid collision, in which case it can not move.
 
                    ;;; This function checks potential horizontal position.
                    ;;; without factoring in vertical motion.  If it sees a solid,
                    ;;; it skips motion.
    ; ***********************
      CheckForHorizontalCollision
    
;;;; This is the end of scrolling platform physics.  It automatically jumps to the proper place.  
;;;; Otherwise, if there was no collision, we jump to updating the position.
    ;; we only need to update horizontal position
    
  

    CheckPlayerCameraPositionHorizontal
;;;; if it turns out we were outside of the camera bounds, the macro
;;;; has RTSed out of this routine, so the hoziontal position will
;;;; never be updated.
   
     JSR updateHorizontalPosition
   
     JMP DoneWithTileChecksAndUpdatePosition
;;;;;;;;;;;;;==============================================  


dontUseGravity:

	CheckPotentialPosition
	CheckPlayerCameraPositionHorizontal
    JSR updateHorizontalPosition
    CheckPotentialPosition
    CheckPlayerCameraPositionVertical
    JSR updateVerticalPosition
	
     JMP DoneWithTileChecksAndUpdatePosition
   
   
   

;;; NO POINTS WERE SOLID    
    ;;;; update hold
DoneWithTileChecksAndUpdatePosition:
   ldx currentObject
    RTS
    
HandleSolidCollision:
	LDA screenFlags
	AND #%00000100 ;; is it autoscrolling?
	BEQ + ;; not autoscrolling
	;;; is auto scrolling
	CPX player1_object
	BNE +
	JSR CheckAutoScrollLeftEdge
	RTS 
+ ;; not auto scrolling.

	LDA xPrev
	STA xHold_hi
	LDA yPrev
	STA yHold_hi
    TYA
    STA tempy
    LDA Object_edge_action,x
    LSR
    LSR
    LSR
    LSR
    BEQ doNothingAtSolid
    TAY
    
    LDA AI_ReactionTable_Lo,y
    STA temp16
    LDA AI_ReactionTable_Hi,y
    STA temp16+1
    
    JSR doReactionTrampolineSolid
    JMP pastReactionTrampolineSolid
doReactionTrampolineSolid:
    JMP (temp16) ;;; this now does the action
            ;; and when it hits the RTS in that action,
            ;; it will jump back to the last JSR it saw,
            ;; which was doNewActionTrampoline...which will immediately
            ;; jump to pastDoNewActionTrampoline.
pastReactionTrampolineSolid:
    ;LDA #$00
    ;STA xHold_lo

doNothingAtSolid:
    ;;;;;;;;;;; Do solid reaction type.
    LDY tempy
    LDX currentObject
    RTS
    
    
ejectUp:
    LDA tileY
    and #%00001111;We can only be 0 to 15 pixels in any given wall
    sta temp
 
    lda Object_y_hi,x
    clc;Subtract one plus that so we're a pixel out of the wall rather than one pixel in it.
    SBC temp
    STA Object_y_hi,x
    LDA #$00
    STA yHold_lo
    STA Object_v_speed_hi,x
    STA Object_v_speed_lo,x
    RTS
    
ejectDown:

    lda tileY
    and #%00001111;we can only be 0 to 15 pixels in any given wall
    eor #%00001111;If we're at position 15 in the tile, we only want to eject 0 (+1) so flip the bits
    sec;Will add the extra one to the position so that we're a pixel out of the wall
    adc Object_y_hi,x;rather than one pixel in it.
    sta Object_y_hi,x
    
    LDA #$00
    STA yHold_lo
    STA Object_v_speed_hi,x
    STA Object_v_speed_lo,x
	
	;;; check the top two collision points to see if is it a prize block.
	LDA collisionPoint0
	CMP #COL_INDEX_PRIZE_BLOCK
	BEQ +
	JMP ++ 
+
	LDA Object_x_hi,x
	CLC
	ADC Object_left,x
	STA tileX
	JMP +++
++
	LDA collisionPoint1
	CMP #COL_INDEX_PRIZE_BLOCK
	BEQ +
	JMP ++
+
	LDA Object_x_hi,x
	CLC
	ADC Object_right,x
	STA tileX
+++


	
	LDA gameHandler
	ORA #%00010000
	STA gameHandler
	;;;;; check other head-hit type objects here.
	;; change collision data.
	;; should still have the correct coordinates in tileX and tileY
	ChangeTileAtCollision #$00, #TILE_INDEX_PRIZE_OFF
	
	TXA
	PHA
	LDA Object_x_hi,x
	STA temp
	LDA Object_y_hi,x
	SEC
	SBC #$12 ;; a bit more than the height of it.
	STA temp1
	LDA Object_scroll,x
	STA temp2
	CreateObject temp, temp1, #OBJ_PRIZE, #$00, temp2
	LDA #$00
	SEC
	SBC #$04
	STA Object_v_speed_hi,x
	
	PLA
	TAX
	
++
    RTS
    
    
    

CheckForCollision:

    ;;;; commonly, we won't want to waste cycling 6 times through this for each object when
    ;;;; all points are at no collision.  If by chance we NEED the zero type collision to do something, 
    ;;;; we can comment out the zero type check in this next line.  But most games are going to have
    ;;;; at least one "blank tile" that does nothing, and most games will use zero for this.
	
	;;;; Another conundrum is that a collision could take place in the current or new nametable.
	;;;; if this collision is of type that draws from screen data (for instance, NPC data or warp data), it could
	;;;; be problematic, as all variables handling these things are loaded with the current collision tables data.
	;;;; tempCol ends up being 0 or 1 based on which collision table this should be referencing.
	
    STA temp
    BNE notZeroTypeCollision
    JMP zeroTypeCollision
notZeroTypeCollision:
    LDA #$00
    STA tile_solidity
DoCheckPointsLoop:
    LDA temp
    TAY
    LDA tileTypeBehaviorLo,y
    STA temp16
    LDA tileTypeBehaviorHi,y
    STA temp16+1
    JSR UseTileTypeTrampoline
    JMP pastTileTypeTrampoline
UseTileTypeTrampoline:
    JMP (temp16)
pastTileTypeTrampoline:
DontCheckTileType0:

    
zeroTypeCollision:
    ldx currentObject
    RTS
    
    
    
    
DetermineCollisionTable:
    LDA tempCol
    BNE colPointInDifferentTable
    ;;;; the collision point is in the current collision table.
    LDA Object_scroll,x
    AND #%00000001
    BNE isInOddTable
    JMP isInEvenTable
colPointInDifferentTable:
    ;;; the collision point to be checked is in the NEXT collision table.
    LDA Object_scroll,x
    AND #%00000001
    BNE isInEvenTable
    JMP isInOddTable
	

isInEvenTable:
    LDA collisionTable,y
    RTS
isInOddTable:
    LDA collisionTable2,y

    RTS


    
    
updateHorizontalPosition:
    LDA xHold_lo
    STA Object_x_lo,x
    LDA xHold_hi
    STA Object_x_hi,x
    LDA nt_hold
	CMP Object_scroll,x
	BEQ justUpdateScroll
	LDA #$01
	STA update_screen_data_flag
	LDA nt_hold
justUpdateScroll:
    STA Object_scroll,x
    
    RTS
    
updateVerticalPosition:
    LDA yHold_lo
    STA Object_y_lo,x
    LDA yHold_hi
    STA Object_y_hi,x   
    RTS

When these 3 scripts are in place and project settings tile collisions is pointing to the correct ASM file above, the screen glitch causes all sorts of unexpected abnormalities, random warping, loss of input control, etc...


I believe there is something needs correcting in the Tile Collision script above as the macros for horizontal/vertical camera checks shouldn't effect collisions. Since the glitch only occurs when the player is touching a tile other than null value when the threshold of the screen is crossed and NOT when a player is touching a monster or object, I believe there is something missing from the Tiles collision code that is present in the Object collision code.
 

Raftronaut

Member
Originally SaturdayXiii had discovered that if you update line 120 in ModuleScripts/MainScripts/TileCollision_4_1_0.asm from "CheckPotentialPosition" to "CheckPotentialPositionScroll" that would prevent the player from losing input when being sucked into the screen edge. Here is the TileCollision_4_1_0.asm after it has been corrected with SaturdayXiii's CheckPotentialPositionScroll
Code:
HandleTileCollision:
    ;;;;;ASSUMES vulnerability bit 000x0000, if one, skips collision.
    ;;;;;Six collision points. 
    ;;;;;All collision points checked and registered for all objects.
    ;;;; a solid in any of the points will result in negating all others.
    
        ;;; There are 6 ram variables, collisionPoint0 - collisionPoint5.
        ;;; collisionPoint0 = top left
        ;;; collisionPoint1 = top right
        ;;; collisionPoint2 = bottom right
        ;;; collisionPoint3 = bottom left.
        ;;; collisionPoint4 = mid left
        ;;; collisionPoint5 = mid right.
    
    TXA 
    STA currentObject
    TYA
    STA tempy

	LDA npc_collision
	AND #%11111101
	LDA #$00
	STA npc_collision
	LDA navFlag
	AND #%11111110
	STA navFlag
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; EXPLANATION:
;; In this basic module, both non gravity and gravity scripts are loaded.
;; Under most circumstances, a user will likely make use of one or the other,
;; as they each take up a good chunk of data.  But the benefit of including both is that
;; a platforming game with gravity is just a simple bit flip in the screen flags.
    
    
    LDA screenFlags
    AND #%00100000
    BNE useGravity
    JMP dontUseGravity
useGravity:
	LDA Object_vulnerability,x
	AND #%00100000
	BEQ useGravity2
	LDA Object_physics_byte,x
	AND #%00000010
	BNE useGravity2
	JMP dontUseGravity
useGravity2:

;;;;;;===================================================
;;;;;;***************************************************
;;;;;; TILE COLLISION FOR SCROLLING PLATFORMERS:
;;;;;; For scrolling platformers we will use three types of collision detection:
;;;;;;          1. A routine to check under our feet to determine
;;;;;;              whether or not you are standing on a solid object.

                    ;;; This function will check under neath the object's feet
                    ;;; by arg0 pixels.
                    ;;; it will update bit 0 of the Object_physics_byte.
                    ;;; If that bit is 0, it means the place is free.
                    ;;; If that bit is 1, it means the place is solid.
    ;**********************
       CheckUnderObject #$01

;;;;;;
;;;;;;          2. A routine that checks to see if the potential position leaves you
;;;;;;              with your 'bottom' or 'top' in a solid object, in which case it ejects pixel perfect
;;;;;;              to that tile.

                    ;;; This function checks the POTENTIAL vertical position.
                    ;;; If it results in a point in a solid object, it appropriately
                    ;;; ejects and places the object directly against the point
                    ;;; of collision.
    ; **********************
     CheckForVerticalEjection 

  
;;;;;;
;;;;;;          3. A routine which checks horizontal motion to see if the potential position
;;;;;;              sees a solid collision, in which case it can not move.
 
                    ;;; This function checks potential horizontal position.
                    ;;; without factoring in vertical motion.  If it sees a solid,
                    ;;; it skips motion.
    ; ***********************
      CheckForHorizontalCollision
    
;;;; This is the end of scrolling platform physics.  It automatically jumps to the proper place.  
;;;; Otherwise, if there was no collision, we jump to updating the position.
    ;; we only need to update horizontal position
    
  

    CheckPlayerCameraPosition
;;;; if it turns out we were outside of the camera bounds, the macro
;;;; has RTSed out of this routine, so the hoziontal position will
;;;; never be updated.
   
     JSR updateHorizontalPosition
   
     JMP DoneWithTileChecksAndUpdatePosition
;;;;;;;;;;;;;==============================================  


dontUseGravity:
;;;;;;===================================================
;;;;;;***************************************************
;;;;;; TILE COLLISION FOR TOP DOWN GAMES: 

;;;; Before we update the position, though, if this is a scrolling game, we need to see if the player
;;;; is at the edge of the camera.  If he is at the edge of the camera, it
;;;; will do a bounds check.
	CheckPlayerCameraPosition
	   
;;;; if it turns out we were outside of the camera bounds, the macro
;;;; has RTSed out of this routine, so the hoziontal position will
;;;; never be updated.
 
;;;;;   For top down type games, or games that generally have no gravity but have 4 directional movement,
;;;;;   we only need to check potential position and jump to the appropriate label.
    CheckPotentialPositionScroll
   



    
    JSR updateHorizontalPosition
    JSR updateVerticalPosition
	
;;;; THis is the end of top down scrolling physics.  It automatically jumps to the proper place.
;;;; Otherwise, if there was no collision, we jump to updating the position.

     JMP DoneWithTileChecksAndUpdatePosition
   
   
   

;;; NO POINTS WERE SOLID    
    ;;;; update hold
DoneWithTileChecksAndUpdatePosition:
   ldx currentObject
    RTS
    
HandleSolidCollision:
	LDA screenFlags
	AND #%00000100 ;; is it autoscrolling?
	BEQ + ;; not autoscrolling
	;;; is auto scrolling
	CPX player1_object
	BNE +
	JSR CheckAutoScrollLeftEdge
	RTS 
+ ;; not auto scrolling.

	LDA xPrev
	STA xHold_hi
	LDA yPrev
	STA yHold_hi
    TYA
    STA tempy
    LDA Object_edge_action,x
    LSR
    LSR
    LSR
    LSR
    BEQ doNothingAtSolid
    TAY
    
    LDA AI_ReactionTable_Lo,y
    STA temp16
    LDA AI_ReactionTable_Hi,y
    STA temp16+1
    
    JSR doReactionTrampolineSolid
    JMP pastReactionTrampolineSolid
doReactionTrampolineSolid:
    JMP (temp16) ;;; this now does the action
            ;; and when it hits the RTS in that action,
            ;; it will jump back to the last JSR it saw,
            ;; which was doNewActionTrampoline...which will immediately
            ;; jump to pastDoNewActionTrampoline.
pastReactionTrampolineSolid:
    ;LDA #$00
    ;STA xHold_lo

doNothingAtSolid:
    ;;;;;;;;;;; Do solid reaction type.
    LDY tempy
    LDX currentObject
    RTS
    
    
ejectUp:
    LDA tileY
    and #%00001111;We can only be 0 to 15 pixels in any given wall
    sta temp
 
    lda Object_y_hi,x
    clc;Subtract one plus that so we're a pixel out of the wall rather than one pixel in it.
    SBC temp
    STA Object_y_hi,x
    LDA #$00
    STA yHold_lo
    STA Object_v_speed_hi,x
    STA Object_v_speed_lo,x
    RTS
    
ejectDown:

    lda tileY
    and #%00001111;we can only be 0 to 15 pixels in any given wall
    eor #%00001111;If we're at position 15 in the tile, we only want to eject 0 (+1) so flip the bits
    sec;Will add the extra one to the position so that we're a pixel out of the wall
    adc Object_y_hi,x;rather than one pixel in it.
    sta Object_y_hi,x
    
    LDA #$00
    STA yHold_lo
    STA Object_v_speed_hi,x
    STA Object_v_speed_lo,x
	
	;;; check the top two collision points to see if is it a prize block.
	LDA collisionPoint0
	CMP #COL_INDEX_PRIZE_BLOCK
	BEQ +
	JMP ++ 
+
	LDA Object_x_hi,x
	CLC
	ADC Object_left,x
	STA tileX
	JMP +++
++
	LDA collisionPoint1
	CMP #COL_INDEX_PRIZE_BLOCK
	BEQ +
	JMP ++
+
	LDA Object_x_hi,x
	CLC
	ADC Object_right,x
	STA tileX
+++


	
	LDA gameHandler
	ORA #%00010000
	STA gameHandler
	;;;;; check other head-hit type objects here.
	;; change collision data.
	;; should still have the correct coordinates in tileX and tileY
	ChangeTileAtCollision #$00, #TILE_INDEX_PRIZE_OFF
	
	TXA
	PHA
	LDA Object_x_hi,x
	STA temp
	LDA Object_y_hi,x
	SEC
	SBC #$12 ;; a bit more than the height of it.
	STA temp1
	LDA Object_scroll,x
	STA temp2
	CreateObject temp, temp1, #OBJ_PRIZE, #$00, temp2
	LDA #$00
	SEC
	SBC #$04
	STA Object_v_speed_hi,x
	
	PLA
	TAX
	
++
    RTS
    
    
    

CheckForCollision:

    ;;;; commonly, we won't want to waste cycling 6 times through this for each object when
    ;;;; all points are at no collision.  If by chance we NEED the zero type collision to do something, 
    ;;;; we can comment out the zero type check in this next line.  But most games are going to have
    ;;;; at least one "blank tile" that does nothing, and most games will use zero for this.
	
	;;;; Another conundrum is that a collision could take place in the current or new nametable.
	;;;; if this collision is of type that draws from screen data (for instance, NPC data or warp data), it could
	;;;; be problematic, as all variables handling these things are loaded with the current collision tables data.
	;;;; tempCol ends up being 0 or 1 based on which collision table this should be referencing.
	
    STA temp
    BNE notZeroTypeCollision
    JMP zeroTypeCollision
notZeroTypeCollision:
    LDA #$00
    STA tile_solidity
DoCheckPointsLoop:
    LDA temp
    TAY
    LDA tileTypeBehaviorLo,y
    STA temp16
    LDA tileTypeBehaviorHi,y
    STA temp16+1
    JSR UseTileTypeTrampoline
    JMP pastTileTypeTrampoline
UseTileTypeTrampoline:
    JMP (temp16)
pastTileTypeTrampoline:
DontCheckTileType0:

    
zeroTypeCollision:
    ldx currentObject
    RTS
    
    
    
    
DetermineCollisionTable:
    LDA tempCol
    BNE colPointInDifferentTable
    ;;;; the collision point is in the current collision table.
    LDA Object_scroll,x
    AND #%00000001
    BNE isInOddTable
    JMP isInEvenTable
colPointInDifferentTable:
    ;;; the collision point to be checked is in the NEXT collision table.
    LDA Object_scroll,x
    AND #%00000001
    BNE isInEvenTable
    JMP isInOddTable
	

isInEvenTable:
    LDA collisionTable,y
    RTS
isInOddTable:
    LDA collisionTable2,y

    RTS


    
    
updateHorizontalPosition:
    LDA xHold_lo
    STA Object_x_lo,x
    LDA xHold_hi
    STA Object_x_hi,x
    LDA nt_hold
	CMP Object_scroll,x
	BEQ justUpdateScroll
	LDA #$01
	STA update_screen_data_flag
	LDA nt_hold
justUpdateScroll:
    STA Object_scroll,x
    
    RTS
    
updateVerticalPosition:
    LDA yHold_lo
    STA Object_y_lo,x
    LDA yHold_hi
    STA Object_y_hi,x   
    RTS

This does not completely correct the problem, but does some good to prevent the player form losing input control, in certain cases being able to move out from the screen edge once getting trapped there.

In any of these cases, the player no longer responds to tile collisions once it is pulled into the screen edge.
The only collisions that seem to check with the player are the Monster collisions. Which leads me to think that there must be something contained in the monster/object collision script that is MISSING from the tile collisions script.

Here is the script my object collisions is pointing to in project settings:
HandleObjectCollisions_DC.asm

Code:
;; LOAD OBJECT 00
;; OUTER LOOP
;; CHECK IF ACTIVE, IF NOT, SKIP OBJECT
;; LOAD self-collision-box
;; START OBJECT COLLISION LOOP
	;; LDA ONE MORE THAN CURRENT OBJECT
	;; CHECK IF ACTIVE.  IF NOT, SKIP THIS other
	;; IF IT IS ACTIVE, then we have to play them against each other.
		;; IS self object hurt by monsters?  If so, and other object is a monster, respond.
		;; IS self object hurt by weapons?  If so, and other object is a weapon, respond.
		;; at this point, we can still gauge whether or not it's affected by player, like with powerups.
		;; ADD one to the object being checked, loop through other objects.
;; increase object, return to outer loop.  Repeat thorugh all self objects.

;; ObjectFlags:
;; 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0
;  |   |   |   |   |   |   |   + -- PERSISTENT (especially good for player!)
;  |   |   |   |   |   |   +------- player type
;  |   |   |   |   |   + ---------- player weapon/projectile type
;  |   |   |   |   +--------------- monster type
;  |   |   |   +------------------- monster weapon/projectile type
;  |   |   +----------------------- pickup / power up
;  |   + -------------------------- target type 
;  +------------------------------- NPC type

;; player type checks monster, mosnter weapon, and pickup.
;; player weapon checks monster and target.
;; nothing else needs checking, as it would all be handled by those two steps.
;; so if it's a monster, do nothing.  if it's a monster projectile, do nothing. 
;; if it's a pickup, or a target, or it ignores all collisions, do nothing.
;; only if it's #%00000110, do something.



HandleObjectCollisions:

	LDA update_screen
	BEQ notChangingScreens
	rts
notChangingScreens:
	
	LDA npc_collision
	AND #%11111110
	STA npc_collision

	;LDA currentBank
	;STA prevBank
	;LDY #BANK_ANIMS
	;JSR bankswitchY
	
	LDX #$00
CollisionOuterLoop:
	TXA
	STA tempx
	LDA Object_status,x
	AND #%10000000
	BNE continueObjectCollisions_objectIsActive
	JMP doneWithThisObjectCollision
continueObjectCollisions_objectIsActive:
	LDA Object_status,x
	AND #%00000100 ;; is ot off screen
	BEQ continueObjectCollisions_objectIsOnScreen
	JMP doneWithThisObjectCollision
continueObjectCollisions_objectIsOnScreen
	LDA Object_status,x
	AND #%00000011
	BEQ continueObjectCollisions_objectIsNotHurtOrInvincible
	JMP doneWithThisObjectCollision
continueObjectCollisions_objectIsNotHurtOrInvincible
	;LDY Object_type,x
	;LDA ObjectFlags,y
	LDA Object_flags,x
	AND #%00000110
	BNE continueObjectCollisions_onlyPlayerTypesCheck
	JMP doneWithThisObjectCollision
continueObjectCollisions_onlyPlayerTypesCheck:
	;; this is either a player or player projectile type of object.
	;; all other types will be taken care of by iterating through these two types.
	;; first, check if it's player type.
	;LdA ObjectFlags,y
	LDA Object_flags,x
	AND #%00000010
	BNE isPlayerTypeForCollision
	JMP notPlayerType_forObjectCollision
isPlayerTypeForCollision:
	LDA player1_object
	STA colX
	;; is player type for object collision
	;; player's index is loaded into tempx
	JSR GetSelfCollisionBox
	;; now we have the collision box for self object
	;; next we loop through objects.
	
	LDX #$00
LoopThroughOtherObjects_player:
	CPX tempx
	BNE dontSkipThisOtherObject
	JMP skipThisOtherObject ;; other object IS the player, the one doing the counting..
dontSkipThisOtherObject:
	LDA Object_status,x
	AND #%00000100
	BEQ dontSkipThisOtherObject_becauseOnScreen
	JMP skipThisOtherObject ;; because it was off screen.
dontSkipThisOtherObject_becauseOnScreen:

	JSR GetOtherCollisionBox
	

	;; now we can do all the compares
	LDA selfNT_R
	CMP otherNT_L
	BCC + ;; no player object collision
	BNE ++ ;; is still possible to see collision.
	LDA selfRight
	CMP otherLeft
	BCC + ;; no player object collision
++ ;; it is still possible there is a collision here.
	LDA otherNT_R
	CMP selfNT_L
	BCC +
	BNE +++
	LDA otherRight
	CMP selfLeft
	BCC +
	
+++ ;; there was a collision here
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	LDA otherBottom
	CMP selfTop
	BCC +
	LDA selfBottom
	CMP otherTop
	BCC +

	JMP DoPlayerObjectCollision

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
+ ;; there is no collision here horizontally.
	JMP noPlayerObjectCollision

DoPlayerObjectCollision:


	
	LDA Object_flags,x
	AND #%10000000 ;; is it an NPC
	BNE isAnNPC
	;; is not an NPC
	JMP isNotAnNPCcollision
isAnNPC:
	;;;; do npc stuff.
	LDA npc_collision
	ORA #%00000001
	STA npc_collision
	;;;; enables a button to be used to activate a textbox.
	LDA Object_ID,x
	STA textVar
	;LDA gameHandler
	;ORA #%00100000
	;STA gameHandler

	JMP skipThisOtherObject
	
isNotAnNPCcollision:	
	LDA Object_flags,x
	;LDA ObjectFlags,y
	AND #%00011000 ;; is it a monster type?
	BNE otherIsAMonsterTypeCollision
	JMP otherIsNotAMonsterTypeCollision
otherIsAMonsterTypeCollision:
	LDA Object_status,x
	AND #HURT_STATUS_MASK ;; if the monster is hurt, it can't hurt us
	BEQ yesPlayerObjectCollision
	JMP noPlayerObjectCollision
yesPlayerObjectCollision:
	
	LDA Object_vulnerability,x
	AND #%00000010 ;; in this module, this is ignore player collision
	BEQ doPlayerHurt
	JMP	noPlayerObjectCollision
doPlayerHurt:
	;;;observe health
	TXA
	STA tempx ;; object is in tempx.
	LDX player1_object
	LDA Object_status,x
	AND #HURT_STATUS_MASK
	BEQ playerWasNotHurtDuringCollision
	JMP playerWasHurtDuringCollision
playerWasNotHurtDuringCollision:
	
	LDA Object_vulnerability,x
	AND #%01000000 ;; is he lethal invincible?
	BNE isLethalInvincible
	JMP notLethalInvincible
isLethalInvincible:
	LDX tempx
	LDA Object_x_hi,x
	STA temp
	LDA Object_y_hi,x
	STA temp1
	CreateObject temp, temp1, #OBJ_MONSTER_DEATH, #$00, currentNametable ;; create "splat"
	LDX tempx
	
	;;; ordinarily we'll want to destroy the instance.
	 DeactivateCurrentObject
	;; incrase score, you killed a monster
	PlaySound #SND_SPLAT
	TXA
	STA tempx
	AddValue #$08, myScore, #$01, #$00

	;;; we also need to set up the routine to update the HUD
	;; for this to work right, health must be a "blank-then-draw" type element.
	;STA hudElementTilesToLoad
	;	LDA #$00
	;	STA hudElementTilesMax
		; LDA DrawHudBytes
		; ora #HUD_myScore
		; STA DrawHudBytes
	UpdateHud HUD_myScore
	LDX tempx
	;;
	;; check for monter locks begin:
	CountObjects #%00001000, #$00
	LDA monsterCounter
	CLC
	BEQ +
	JMP ++
	+
		.include SCR_KILLED_LAST_MONSTER
	;; check for monter locks end.
	++	
	JMP skipThisOtherObject
	
notLethalInvincible:
	
	;;;;;;;;;;;;;;;;;
	;;;;;;;;; WHAT HAPPENS WHEN PLAYER IS HURT
	.include SCR_PLAYER_HURT_SCRIPT
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
	
playerWasHurtDuringCollision:	
	LDX tempx
	JMP skipThisOtherObject
otherIsNotAMonsterTypeCollision:
	;LDA ObjectFlags,y
	LDA Object_flags,x
	AND #%00100000 ;; is it a 'collectable'?
	BEQ otherIsNotAcollectable
	;;;; IS A pickup / power up
	DeactivateCurrentObject ;; makes the other object go away
							;; since other object is loaded in X
							
;;=========== WHAT DO YOU WANT TO HAVE HAPPEN WHEN YOU COLLECT THIS ITEM?

	JSR HandlePickupPowerup

	
otherIsNotAcollectable:
noPlayerObjectCollision:	
skipThisOtherObject:
	INX
	CPX #TOTAL_MAX_OBJECTS
	BEQ doneLoopThroughOtherObjects_player
	JMP LoopThroughOtherObjects_player
doneLoopThroughOtherObjects_player:
	;; end of player collision
	LDX tempx ;; restore x
	JMP doneWithThisObjectCollision
	
	
	
notPlayerType_forObjectCollision:
	;; is of player weapon type.
	JSR GetSelfCollisionBox
	;; now we have the collision box for self object
	;; next we loop through objects.
	LDX #$00
LoopThroughOtherObjects_weapon:

	CPX tempx
	BNE dontskipThisOtherObject_weapon
	JMP skipThisOtherObject_weapon
dontskipThisOtherObject_weapon
	JSR GetOtherCollisionBox
	;; now we can do all the compares
	LDA selfNT_R
	CMP otherNT_L
	BCC + ;; no player object collision
	BNE ++ ;; is still possible to see collision.
	LDA selfRight
	CMP otherLeft
	BCC + ;; no player object collision
++ ;; it is still possible there is a collision here.
	LDA otherNT_R
	CMP selfNT_L
	BCC +
	BNE +++
	LDA otherRight
	CMP selfLeft
	BCC +
	
+++ ;; there was a collision here
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	LDA otherBottom
	CMP selfTop
	BCC +
	LDA selfBottom
	CMP otherTop
	BCC +

	JMP doWeaponObjectCollision

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
+ ;; there is no collision here horizontally.
	JMP noWeaponObjectCollision
doWeaponObjectCollision:
	;; go through the different types of collision possible.
	;; first, check monster OR monser projectile, as that should lead to hurt/death
	;LDY Object_type,x
	;LDA ObjectFlags,y
	LDA Object_flags,x
	AND #%00001000 ;; is it a monster type?
	;;; if you'd like the player weapon to ALSO destroy projectiles
	;;; use #%00011000 here
	BNE otherIsMonsterTypeCollision_weapon
	JMP otherIsNotAMonsterTypeCollision_weapon
otherIsMonsterTypeCollision_weapon:
;;;;;;;;;;;;;;;;;;;;;;;;
	TXA
	PHA
	.include SCR_HANDLE_HURT_MONSTER
	PLA
	TAX
	;;; if monster dies, count monsters
	;; right now, he always dies, so count the monsters.
	JSR countAllMonsters	
	

	
otherIsNotAMonsterTypeCollision_weapon:
	
noWeaponObjectCollision:	
skipThisOtherObject_weapon:
	INX
	CPX #TOTAL_MAX_OBJECTS
	BEQ doneWithLoopingThroughWeaponObjects
	JMP LoopThroughOtherObjects_weapon
doneWithLoopingThroughWeaponObjects:
	
	
	
	;; end of player collision
	LDX tempx ;; restore x
	JMP doneWithThisObjectCollision
	
	
	
	
doneWithThisObjectCollision:
	LDX tempx
	INX
	CPX #TOTAL_MAX_OBJECTS
	BEQ doneWithAllObjects
	JMP CollisionOuterLoop
doneWithAllObjects:
	;;;;;;;;;;;;;;;;;;;;;;;;;
	;; for this module
	;; we will check for the melee position.
	;; but rather than waste space with an entire object
	;; we'll just test it against a single point
	.include SCR_CHECK_SPRITE_WEAPON

	RTS
	
	
	
	
	
	
GetSelfCollisionBox:	
	LDA Object_x_hi,x
	CLC
	ADC Object_left,x
	STA selfLeft
	LDA Object_scroll,x
	ADC #$00
	STA selfNT_L
	
	LDA Object_x_hi,x
	CLC
	ADC Object_right,x
	STA selfRight
	LDA Object_scroll,x
	ADC #$00
	STA selfNT_R
	
	LDA Object_vulnerability,x
	AND #%10000000
	BEQ noDuckingBit
	LDA Object_bottom
	SEC 
	SBC Object_top
	STA temp
	LDA Object_y_hi,x
	CLC
	ADC temp
	JMP gotSelfTop
noDuckingBit:
	LDA Object_y_hi,x
	CLC
	ADC Object_top,x
gotSelfTop:
	STA selfTop
	LDA Object_y_hi,x
	CLC
	ADC Object_bottom,x
	STA selfBottom
	LDA Object_x_hi,x
	CLC
	ADC Object_origin_x,x
	STA selfCenterX
	LDA Object_y_hi,x
	CLC
	ADC Object_origin_y,x
	STA selfCenterY
	

	RTS
	
GetOtherCollisionBox:
	LDA Object_x_hi,x
	CLC
	ADC Object_left,x
	STA otherLeft
	LDA Object_scroll,x
	ADC #$00
	STA otherNT_L
	
	LDA Object_x_hi,x
	CLC
	ADC Object_right,x
	STA otherRight
	LDA Object_scroll,x
	ADC #$00
	STA otherNT_R
	
	LDA Object_vulnerability,x
	AND #%10000000
	BEQ noDuckingBit_other
	LDA Object_bottom
	SEC 
	SBC Object_top
	STA temp
	LDA Object_y_hi,x
	CLC
	ADC temp
	JMP gotSelfTop_other
noDuckingBit_other:
	LDA Object_y_hi,x
	CLC
	ADC Object_top,x
gotSelfTop_other:	
	
	STA otherTop
	LDA Object_y_hi,x
	CLC
	ADC Object_bottom,x
	STA otherBottom
	LDA Object_x_hi,x
	CLC
	ADC Object_origin_x,x
	STA otherCenterX
	LDA Object_y_hi,x
	CLC
	ADC Object_origin_y,x
	STA otherCenterY
	

	RTS
	
	
	
	
	
	
	
DetermineRecoilDirection:

	;;;RECOIL
	;;First check for the abs x value
	LDA recoil_selfX
	SEC
	SBC recoil_otherX
	BCS absCheckDone
	EOR #$FF
	CLC
	ADC #$01
absCheckDone:
	STA temp
	LDA recoil_selfY
	SEC
	SBC recoil_otherY
	BCS absCheckDone2
	EOR #$FF
	CLC
	ADC #$01
absCheckDone2:
	CMP temp
	BCS vCol
	LDA recoil_selfX
	CMP recoil_otherX
	BCS recoilRight
	;; recoil left
	;LDX #$01
	LDA #RECOIL_SPEED_LO
	STA Object_h_speed_lo,x
	LDA #$00
	SEC
	SBC	#RECOIL_SPEED_HI
	STA Object_h_speed_hi,x
	LDA #$00
	STA Object_v_speed_hi,x
	STA Object_v_speed_lo,x
	LDA #%10000000
	STA temp1
	LDA Object_movement,x
	AND #%00000111
	ORA temp1
	STA Object_movement,x
	CPX player1_object
	BNE dontChangeScrollDirectionL
	LDA #$00
	STA scrollDirection
dontChangeScrollDirectionL
	RTS
	
recoilRight:
	;LDX #$01
	LDA #RECOIL_SPEED_LO
	STA Object_h_speed_lo,x
	LDA	#RECOIL_SPEED_HI
	STA Object_h_speed_hi,x
	LDA #$00
	STA Object_v_speed_hi,x
	STA Object_v_speed_lo,x
	LDA #%11000000
	STA temp1
	LDA Object_movement,x
	AND #%00000111
	ORA temp1
	STA Object_movement,x
	CPX player1_object
	BNE dontChangeScrollDirectionR
	LDA #$01
	STA scrollDirection
dontChangeScrollDirectionR:
	RTS
	
vCol:
	LDA recoil_selfY
	CMP recoil_otherY
	BCS recoilDown
	;LDX #$01
	LDA #RECOIL_SPEED_LO
	STA Object_v_speed_lo,x
	LDA #$00
	SEC
	SBC	#RECOIL_SPEED_HI
	STA Object_v_speed_hi,x
	LDA #%00100000
	STA temp1
	LDA #$00
	STA Object_h_speed_hi,x
	STA Object_h_speed_lo,x
	LDA Object_movement,x
	AND #%00000111
	ORA temp1
	STA Object_movement,x

	RTS
	
recoilDown:
	;LDX #$01
	LDA #RECOIL_SPEED_LO
	STA Object_v_speed_lo,x
	LDA #RECOIL_SPEED_HI
	STA Object_v_speed_hi,x
	LDA #%00110000
	STA temp1
	LDA #$00
	STA Object_h_speed_hi,x
	STA Object_h_speed_lo,x
	LDA Object_movement,x
	AND #%00000111
	ORA temp1
	STA Object_movement,x
	
	RTS

I am hoping to drop all this code here for ease of access so I can disect as availability permits.
I've been studying ChronosV2's asm tutorials on youtube and taking notes, unfortunately my comprehension level of ASM is still very limited.

Any and ALL pointers here would be appreciated!!!
 

Raftronaut

Member
Code:
I was thinking to myself today that since this is an autoscrolling shooter, a simple fix would be to add a line in the for this would I could simply add a command into the left boundshandler's script to DoPLayerDeath. By essentially destroying the character at the left bounds that would eliminate the need to find the bug, or at least would be a cheap cheap fix in the meantime..

Here is the script DoLeftBoundsUpdate.asm thaT HANDLE LEFT BOUNDS is pointing to in project settings:
Code:
	LDA screenFlags
	AND #%00000010
	BEQ +
	
	;LDA screenFlags
	;AND #%00000100 ;; left bounds autoscrolling right
	;BEQ +
	LDA #$00
	STA Object_x_lo,x
	STA Object_h_speed_lo,x
	STA Object_h_speed_hi,x
	; for auto scrolling, use xScroll instead of xPrev? 
	;LDA xScroll
	LDA xPrev
	STA xHold_lo
	STA Object_x_hi,x
	STA xHold_hi
;	LDA xScroll_hi
;	STA Object_scroll,x
;	STA nt_hold


	JMP doneWithUpdateLeftScreen
	
+
	
	LDA #BOUNDS_RIGHT
	SEC
	SBC Object_right,x
	SEC 
	SBC #$02

	STA xHold_hi
	STA newX
	STA Object_x_hi,x
	
	LDA Object_y_hi,x
	STA newY
	
		LDA #$00
	STA xHold_lo
	STA yHold_lo

;;; check if side triggers change screen...for instance, if hurt, wouldn't, and would instead return *solid*

	
	LDA Object_scroll,x
			sec
			sbc #$01
			STA Object_scroll,x
			STA currentNametable
			STA newScreen
			STA currentScreen ;currentScreen
			STA xScroll_hi
			
			clc
			adc #$01
			STA rightNametable
			SEC
			SBC #$02
			STA leftNametable
			
			LDA Object_scroll,x
			AND #%00000001
			STA showingNametable
			
			
			LDA #$01
			STA screen_transition_type
			
		;LDA warpMap
			;CLC
			;ADC #$01
			;STA update_screen_details
			
			;;; update screen details will not change with left, right, up or down movement
			;;; unless the edge of a map should take you to the other map or something.
			;;; warpMap variable is used solely to hold whether the warp-out is to overworld or underworld.
			

		
	LDA #$01
	STA tile_solidity		
	LDA #$00
	STA gameHandler
		LDA #%11000000
				;7 = active
				;6 = 8 or 16 px tiles
			ORA #GS_MainGame
			ORA #%01000000
			STA update_screen

doneWithUpdateLeftScreen:
 

dale_coop

Moderator
Staff member
For scrolling screens, this script is not used. I think the Handle Camera and the Handle Scroll must be doing all the job.
 

Raftronaut

Member
dale_coop said:
For scrolling screens, this script is not used. I think the Handle Camera and the Handle Scroll must be doing all the job.

Ahh, thank you for the insight Dale, I will poke around in those scripts. Mugi had suggested some changes in Handle player camera position which didn't seem to work for the scrolling engine, but I didn't think to look there further.

I created a suicide button in the meantime, so at least now when the glitch does occur, you can simply reset the player to the last checkpoint rather than reset the program. I'm not thrilled about it, but It's a cheap fix for now while I am working on debugging. A minor victory is that the script I created to be assigned as an input command worked the first time I implemented it, a first for me on the scripting side of Nesmaker!
 

Raftronaut

Member
IN the previous thread about this glitch, saturdayxiii mentioned:
Here's an interesting development. There exists a macro called CheckPotentialPositionScroll, also one called CheckPotentialPositionSafe but it seemed to act the same. Before applying Mugi's supplied code, I go into ModuleScripts/MainScripts/TileCollision_4_1_0.asm and change line 120 "CheckPotentialPosition" to "CheckPotentialPositionScroll", then the ship no longer gets stuck in the wall. It still passes through the instant death tiles, and even appears to get stuck in the screen edge, but I can move out and continue playing without breaking the game completely. That's an improvement IMO.

I'm looking deeper into macro to Check CheckPotentialPositionScroll, since changing this part of my tile collision script did help reduce the cases the glitch occurred.
Code:
MACRO CheckPotentialPositionScroll:

;;;; RESET SOLID
	LDA #$00
    STA tile_solidity
	Sta collisionPoint0
	STA collisionPoint1
	STA collisionPoint2
	STA collisionPoint3
	STA collisionPoint4
	STA collisionPoint5
	
;;; RESET LADDER STATE	
	LDA Object_physics_byte,x
	AND #%11111101
	STA Object_physics_byte,x
	
	LDA Object_h_speed_lo,x
	CLC
	ADC #$00
	LDA Object_h_speed_hi,x
	ADC #$00
	BMI doLeftCollisionCheck
	JMP dontDoLeftCollisionCheck ;; if moving right, skip left check
doLeftCollisionCheck:
	;;;;;;;;;;;;;;;;;;;;;;;;; CHECK ALL LEFT POINTS
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	;LDA Object_left,x
	;LDA #$00
	;STA temp
	;DetermineCollisionTableOfPoints temp
	
    LDA xHold_hi
    CLC
    ADC Object_left,x
    STA tileX

    LDA yHold_hi
    CLC
    ADC Object_top,x
    STA tileY
	
    JSR GetTileAtPosition
    ;JSR DetermineCollisionTable
	LDA #$00
	STA temp
	DetermineCollisionTableOfPoints temp
	
    STA collisionPoint0
    JSR CheckForCollision
    LDA tile_solidity
	AND #%00000001
    BEQ +
    JMP HandleSolidCollision ;; hit a solid so won't update position. 
+   
	;;;; LEFT BOTTOM
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    LDA xHold_hi
    CLC
    ADC Object_left,x
    STA tileX

    LDA yHold_hi
    CLC
    ADC Object_bottom,x
    STA tileY
    
    JSR GetTileAtPosition
    ;JSR DetermineCollisionTable
	LDA #$00
	STA temp
	DetermineCollisionTableOfPoints temp
    STA collisionPoint3
    JSR CheckForCollision
    LDA tile_solidity
	AND #%00000001
    BEQ +
    ;LDA Object_physics_byte,x
    ;ORA #%01000000 ;; solid was at top.
    ;STA Object_physics_byte,x
    JMP HandleSolidCollision ;; hit a solid so won't update position.
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;; LEFT MIDDLE
	LDA Object_bottom,x
    SEC
    SBC Object_top,x
    LSR 
    STA temp3 ;; temp 3 now equals half of the height of the object, so top+temp3 = mid point vertically.
    
	LDA xHold_hi
    CLC
    ADC Object_left,x
    STA tileX

    LDA yHold_hi
    CLC
    ADC Object_top,x
    CLC
    ADC temp3
    STA tileY
    
    JSR GetTileAtPosition
  ;  JSR DetermineCollisionTable
  	LDA #$00
	STA temp
	DetermineCollisionTableOfPoints temp
    STA collisionPoint4
    JSR CheckForCollision
    LDA tile_solidity
	AND #%00000001
    BEQ +
    JMP HandleSolidCollision ;; hit a solid so won't update position.
+

	JMP DoneWithPotentialPositionCheck
dontDoLeftCollisionCheck:
;;; RIGHT COLLISION CHECKS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

   ; LDA xHold_hi
 ;   CLC
	LDA Object_x_lo,x
	CLC
	ADC Object_h_speed_lo,x
	LDA Object_x_hi,x
	ADC Object_h_speed_hi,x
    ADC Object_right,x
    STA tileX

    LDA yHold_hi
    CLC
    ADC Object_top,x
    STA tileY
    
    JSR GetTileAtPosition
   ; JSR DetermineCollisionTable
   	LDA Object_right,x
	STA temp
	DetermineCollisionTableOfPoints temp
	
    STA collisionPoint1
    JSR CheckForCollision
    LDA tile_solidity
	AND #%00000001
    BEQ +
    JMP HandleSolidCollision ;; hit a solid so won't update position.
+



    LDA xHold_hi
    CLC
    ADC Object_right,x
    STA tileX
   
    LDA yHold_hi
    CLC
    ADC Object_bottom,x

    STA tileY
    
    JSR GetTileAtPosition
   ; JSR DetermineCollisionTable
   	LDA Object_right,x
	STA temp
	DetermineCollisionTableOfPoints temp
    STA collisionPoint2
    JSR CheckForCollision
    LDA tile_solidity
	AND #%00000001
    BEQ +
    ;LDA Object_physics_byte,x
    ;ORA #%01000000 ;; solid was at top.
    ;STA Object_physics_byte,x
    JMP HandleSolidCollision ;; hit a solid so won't update position.
+



    ;;; IF YOU NEED TO FIND OUT THE MID POINT HORIZONTALLY
    ;LDA Object_right,x
    ;SEC
    ;SBC Object_left,x
    ;LSR
    ;STA temp2 ;; temp 2 now equals half the width of the object, so left+temp2 = mid point horizontally.
    
    LDA Object_bottom,x
    SEC
    SBC Object_top,x
    LSR 
    STA temp3 ;; temp 3 now equals half of the height of the object, so top+temp3 = mid point vertically.
    
	

    LDA yHold_hi
    CLC
    ADC Object_top,x
    CLC
    ADC temp3
    STA tileY

	LDA xHold_hi
    CLC
    ADC Object_right,x
    STA tileX
     
    JSR GetTileAtPosition
  ;  JSR DetermineCollisionTable
  	LDA Object_right,x
	STA temp
	DetermineCollisionTableOfPoints temp

    STA collisionPoint5
    JSR CheckForCollision
    LDA tile_solidity
	AND #%00000001
    BEQ +
    JMP HandleSolidCollision ;; hit a solid so won't update position.
+
DoneWithPotentialPositionCheck:
	ENDM
lines 25-118 seem to be handling left side collisions. Maybe there is something noteworthy there. Though I imagine this has to do directly with the tile's routine for collision rather than where the player in within the playfeild bounds.


Or, Handle camera
Code:
HandleCamera:


	LDA xScroll_hi
	STA camL_hi
	LdA xScroll
	STA camL_lo
	
	LDA xScroll
	CLC
	ADC #$FF
	
	STA camR_lo
	LDA camL_hi
	ADC #$00
	STA camR_hi
	;;;;;;;;;;;;;;;;;;;;;;;;
	;; This is where monsters are gauge to have reached the 'edge' of existable space.
	
	LDA camL_lo
	SEC
	SBC #MONSTER_CAMERA_PAD
	STA camPad_L_lo
	LDA camL_hi
	SBC #$00
	STA camPad_L_hi
	
	LDA camR_lo
	CLC
	ADC #MONSTER_CAMERA_PAD
	STA camPad_R_lo
	LDA camR_hi
	ADC #$00
	STA camPad_R_hi

	
	CPX player1_object
	BNE doHandleCameraBoundsCheck
	RTS
doHandleCameraBoundsCheck:

	JSR HandleCameraBoundsCheck
	RTS

	
	
	;;; HANDLE EDGE BEHAVIOR
DoMonsterEdgeBehavior:	

	;JSR StopAtEdge
	LDA Object_edge_action,x
	AND #%00001111
	BEQ doNothingAtEdge
	STA temp
	
	LDY temp
	LDA AI_ReactionTable_Lo,y
	STA temp16
	LDA AI_ReactionTable_Hi,y
	STA temp16+1
	

	
	JSR doReactionTrampoline
	JMP pastReactionTrampoline
doReactionTrampoline:
	JMP (temp16) ;;; this now does the action
			;; and when it hits the RTS in that action,
			;; it will jump back to the last JSR it saw,
			;; which was doNewActionTrampoline...which will immediately
			;; jump to pastDoNewActionTrampoline.
pastReactionTrampoline:

doNothingAtEdge:

	RTS
	
	
	
	
StopAtEdge:
	LDA xPrev
	STA Object_x_hi,x
	STA xHold_hi
	LDA yPrev
	STA Object_y_hi,x
	STA yHold_hi
	LDA #$00
	STA Object_x_lo,x
	STA xHold_lo
	STA Object_y_lo,x
	STA xHold_lo
	
	STA Object_h_speed_hi,x
	STA Object_h_speed_lo,x
	STA Object_v_speed_hi,x
	STA Object_v_speed_lo,x
	
	LDA Object_movement,x
	AND #%00001111
	STA Object_movement,x
	RTS
	
	
HandleMonsterPadCheck
;;;; CHECK AGAINST CAM LEFT.
	;JMP +
	LDA xHold_hi
	CLC
	ADC Object_left,x
	LDA Object_scroll,x
	ADC #$00
	STA temp
	
	LDA temp
	CMP camPad_L_hi
	BCC ObjectAtPadEdge
	BNE isNotLeftOfPad
	LDA xHold_hi
	CLC
	ADC Object_left,x
	CMP camPad_L_lo
	BCC ObjectAtPadEdge
isNotLeftOfPad:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; CHECK AGAINST CAM RIGHT
	LDA xHold_hi
	CLC
	ADC Object_right,x
	LDA Object_scroll,x
	ADC #$00
	STA temp
	LDA camPad_R_hi
	CMP temp
	BCC ObjectAtPadEdge
	BNE isNotRightOfPad
	LDA xHold_hi
	CLC
	ADC Object_right
	STA temp
	LDA camPad_R_lo
	CMP temp
	BCC ObjectAtPadEdge
isNotRightOfPad:
	
	RTS
	
ObjectAtPadEdge:

	DeactivateCurrentObject
	RTS
	
HandleCameraBoundsCheck
	;;; first, check top and bottom.
	

	
	LDA yHold_hi
	CLC
	ADC Object_top,x
	CMP #BOUNDS_TOP
	BCS +
	JMP notOutsideOfCamera ;; this jumps to handling monster edge reaction
+
	LDA yHold_hi
	CLC
	ADC Object_bottom,x
	CMP #BOUNDS_BOTTOM
	BCC +
	JMP notOutsideOfCamera ;; this jumps to handling monster edge reaction
+
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; CHECK AGAINST CAM LEFT.
	CPX player1_object
	BNE +
	LDA xScroll_hi
	BNE + ; not on screen 0
	;; is on screen zero

	LDA xScroll
	CLC
	ADC Object_left,x
	CMP xHold_hi
	BCS ObjectAtCameraEdge
+

	LDA xHold_hi
	CLC
	ADC Object_left,x
	LDA Object_scroll,x
	ADC #$00

	CMP camL_hi
	BCC ObjectAtCameraEdge
	BNE isNotLeftOfCam
	; LDA xHold_hi
	; CLC
	; ADC Object_left,x
	; CMP camL_lo
	; BCC ObjectAtCameraEdge
	LDA camL_lo
	CLC
	ADC Object_left,x
	CMP xHold_hi
	BCS ObjectAtCameraEdge
	
	JMP isNotLeftOfCam
isNotLeftOfCam:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; CHECK AGAINST CAM RIGHT
	LDA xHold_hi
	CLC
	ADC Object_right,x
	LDA Object_scroll,x
	ADC #$00
	STA temp

	LDA camR_hi
	CMP temp
	BCC ObjectAtCameraEdge
	BNE isNotRightOfCam
	LDA xHold_hi
	CLC
	ADC Object_right,x
	STA temp
	LDA camR_lo
	CMP temp
	BCC ObjectAtCameraEdge
isNotRightOfCam:

	LDA Object_status,x
	AND #%11111011
	STA Object_status,x
	;; show this object
	RTS
ObjectAtCameraEdge:
	LDA Object_status,x
	AND #%00000100
	BEQ notOutsideOfCamera
	JSR HandleMonsterPadCheck
	;;; this object is outside of camera.
	RTS
notOutsideOfCamera:
	JSR DoMonsterEdgeBehavior
	RTS
Here, the doplayercamercabounds check around line 38 seems of interest. I am wondering if there is a section in the code there where I can insert a DoPlayerDeath command when the player is recognized as overlapping with the left screen bounds. If this worked it would essentially solve my issues...
 

Raftronaut

Member
My handle camera script is here, I suspect there is something missing between lines 179-208 handling camera left position. I am slowly starting to understand some basic ASM commands, but the addresses and routines call here for the scrolling are beyond me. I was hoping to compare the left camera to the right (which has not shown yet to be broken) to see if I isolate the problem.

Code:
HandleCamera:


	LDA xScroll_hi
	STA camL_hi
	LdA xScroll
	STA camL_lo
	
	LDA xScroll
	CLC
	ADC #$FF
	
	STA camR_lo
	LDA camL_hi
	ADC #$00
	STA camR_hi
	;;;;;;;;;;;;;;;;;;;;;;;;
	;; This is where monsters are gauge to have reached the 'edge' of existable space.
	
	LDA camL_lo
	SEC
	SBC #MONSTER_CAMERA_PAD
	STA camPad_L_lo
	LDA camL_hi
	SBC #$00
	STA camPad_L_hi
	
	LDA camR_lo
	CLC
	ADC #MONSTER_CAMERA_PAD
	STA camPad_R_lo
	LDA camR_hi
	ADC #$00
	STA camPad_R_hi

	
	CPX player1_object
	BNE doHandleCameraBoundsCheck
	RTS
doHandleCameraBoundsCheck:

	JSR HandleCameraBoundsCheck
	RTS

	
	
	;;; HANDLE EDGE BEHAVIOR
DoMonsterEdgeBehavior:	

	;JSR StopAtEdge
	LDA Object_edge_action,x
	AND #%00001111
	BEQ doNothingAtEdge
	STA temp
	
	LDY temp
	LDA AI_ReactionTable_Lo,y
	STA temp16
	LDA AI_ReactionTable_Hi,y
	STA temp16+1
	

	
	JSR doReactionTrampoline
	JMP pastReactionTrampoline
doReactionTrampoline:
	JMP (temp16) ;;; this now does the action
			;; and when it hits the RTS in that action,
			;; it will jump back to the last JSR it saw,
			;; which was doNewActionTrampoline...which will immediately
			;; jump to pastDoNewActionTrampoline.
pastReactionTrampoline:

doNothingAtEdge:

	RTS
	
	
	
	
StopAtEdge:
	LDA xPrev
	STA Object_x_hi,x
	STA xHold_hi
	LDA yPrev
	STA Object_y_hi,x
	STA yHold_hi
	LDA #$00
	STA Object_x_lo,x
	STA xHold_lo
	STA Object_y_lo,x
	STA xHold_lo
	
	STA Object_h_speed_hi,x
	STA Object_h_speed_lo,x
	STA Object_v_speed_hi,x
	STA Object_v_speed_lo,x
	
	LDA Object_movement,x
	AND #%00001111
	STA Object_movement,x
	RTS
	
	
HandleMonsterPadCheck
;;;; CHECK AGAINST CAM LEFT.
	;JMP +
	LDA xHold_hi
	CLC
	ADC Object_left,x
	LDA Object_scroll,x
	ADC #$00
	STA temp
	
	LDA temp
	CMP camPad_L_hi
	BCC ObjectAtPadEdge
	BNE isNotLeftOfPad
	LDA xHold_hi
	CLC
	ADC Object_left,x
	CMP camPad_L_lo
	BCC ObjectAtPadEdge
isNotLeftOfPad:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; CHECK AGAINST CAM RIGHT
	LDA xHold_hi
	CLC
	ADC Object_right,x
	LDA Object_scroll,x
	ADC #$00
	STA temp
	LDA camPad_R_hi
	CMP temp
	BCC ObjectAtPadEdge
	BNE isNotRightOfPad
	LDA xHold_hi
	CLC
	ADC Object_right
	STA temp
	LDA camPad_R_lo
	CMP temp
	BCC ObjectAtPadEdge
isNotRightOfPad:
	
	RTS
	
ObjectAtPadEdge:

	DeactivateCurrentObject
	RTS
	
HandleCameraBoundsCheck
	;;; first, check top and bottom.
	

	
	LDA yHold_hi
	CLC
	ADC Object_top,x
	CMP #BOUNDS_TOP
	BCS +
	JMP notOutsideOfCamera ;; this jumps to handling monster edge reaction
+
	LDA yHold_hi
	CLC
	ADC Object_bottom,x
	CMP #BOUNDS_BOTTOM
	BCC +
	JMP notOutsideOfCamera ;; this jumps to handling monster edge reaction
+
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; CHECK AGAINST CAM LEFT.
	CPX player1_object
	BNE +
	LDA xScroll_hi
	BNE + ; not on screen 0
	;; is on screen zero

	LDA xScroll
	CLC
	ADC Object_left,x
	CMP xHold_hi
	BCS ObjectAtCameraEdge
+

	LDA xHold_hi
	CLC
	ADC Object_left,x
	LDA Object_scroll,x
	ADC #$00

	CMP camL_hi
	BCC ObjectAtCameraEdge
	BNE isNotLeftOfCam
	; LDA xHold_hi
	; CLC
	; ADC Object_left,x
	; CMP camL_lo
	; BCC ObjectAtCameraEdge
	LDA camL_lo
	CLC
	ADC Object_left,x
	CMP xHold_hi
	BCS ObjectAtCameraEdge
	
	JMP isNotLeftOfCam
isNotLeftOfCam:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; CHECK AGAINST CAM RIGHT
	LDA xHold_hi
	CLC
	ADC Object_right,x
	LDA Object_scroll,x
	ADC #$00
	STA temp

	LDA camR_hi
	CMP temp
	BCC ObjectAtCameraEdge
	BNE isNotRightOfCam
	LDA xHold_hi
	CLC
	ADC Object_right,x
	STA temp
	LDA camR_lo
	CMP temp
	BCC ObjectAtCameraEdge
isNotRightOfCam:

	LDA Object_status,x
	AND #%11111011
	STA Object_status,x
	;; show this object
	RTS
ObjectAtCameraEdge:
	LDA Object_status,x
	AND #%00000100
	BEQ notOutsideOfCamera
	JSR HandleMonsterPadCheck
	;;; this object is outside of camera.
	RTS
notOutsideOfCamera:
	JSR DoMonsterEdgeBehavior
	RTS
 

Raftronaut

Member
I found something interesting today. I was messing around with scroll speeds in User Constants. My current scroll speed is set to 2, if I set it to 1 it is too slow for my liking, but drastically reduces the opportunities for the player to be in position to cause the glitch to occur. I then tested it at user constant speed 3 where I discovered that the player will wrap into the screen edge even without controller input as previously concluded. So touching the screen edge and any solid collision at the same time while using scroll speed 3 guarantees the glitch occurs.

I wonder why increasing the speed of the scrolling would effect collisions so drastically like this. Maybe collisions are being updated at the screen left bounds edge in the wrong order perhaps.
https://youtu.be/_eqNAL39Gp4
Assuming that User Constant Scroll Speed would be a variable present in or at least pointing to the HandleScroll.asm I wondering if there is a collision check that is out of order here:
Code:
	LDA gameHandler
	AND #%10000000
	BNE mainGameStateLoaded
	RTS
mainGameStateLoaded:
	LDA screenFlags
	AND #%10000000
	BEQ +
	;;; This is a single screen game.
	;;; advised simply for speed and ppu update skips.
	RTS
+
	LDA prevent_scroll_flag
	BEQ canDoScrollUpdate
	
	RTS

canDoScrollUpdate:
;	LDA gameHandler
;	AND #%00010000
;	BEQ notUpdatingOtherTiles
;	RTS
notUpdatingOtherTiles:

    LDA OverwriteNT_column
    BEQ dontDoScrollUpdateYet
	
;;;; is already scrolling.
    JMP doScrollTileUpdate
dontDoScrollUpdateYet:
	LDA forceScroll
	AND #%00000010
	BNE doForceScroll
	
	LDA forceScroll
	AND #%00000001
	BEQ dontForceScroll
	
	LDA updateNametable
	BNE dontForceScroll
	
	LDA #%00000010
	STA forceScroll

	RTS ;; gives a one frame cushion before drawing new column.
dontUpdateForceScrollVar:


	;JMP doScrollTileUpdate
doForceScroll:

	LDA #$00
	STA forceScroll

   JMP skipSettingNewNT ;; so, when the game starts, we need to pretend
                        ;; there is a scroll for position 0.
                        ;; otherwise we'll get a seam.
                        
                        ;; Similarly, we need to force a scroll
                        ;; if we change direction mid-column
                        ;; so we don't get a seam.
dontForceScroll:                        

    ;;;;;;;;;;;;;;;;;;;;; column tracker is zero at the beginning of the game.
    ;;;;;;;;;;;;;;;;;;;; it should also be set to zero at a room load*****

    LDA xScroll
    LSR
    LSR
    LSR
    LSR
    STA scrollColumn ;; this now keeps track of the column out of 32
                    ;; first 16 columns being in the left nt
                    ;; second 16 columns being in the right nt
    
    LDA columnTracker
    AnD #%00001111  ;; what column are we on, in whichever nametable we're on.
    CMP scrollColumn
    BNE timeToUpdateScrollColumn

    JMP skipScroll_notPlayer
timeToUpdateScrollColumn:

    LDA #$01
    STA ObjectSeamLoadFlag
    LDA #$00
    STA objectID_forSeamLoad
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; first, update the columnTracker (or should this happen at the end?)
    LDA scrollDirection
    BEQ scrollDirLeft
    ;;;;;;;;;;;;;;;;;;;;;;
    inc columnTracker
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; WE WILL LOOK two COLUMNs AHEAD OF THE RIGHTMOST COLUMN
;;;;; TO SEE WHAT OBJECTS NEED TO BE LOADED.
    LDA columnTracker
    CLC
    ADC #$10
    AND #%00011111
    STA scroll_hold
    
    LDA columnTracker
    AND #%00001111
    ;BNE gotColumnTrackerUpdate
    ;; just reset to zero
    ;INC xScroll_hi
    JMP gotColumnTrackerUpdate
scrollDirLeft:
    dec columnTracker
	
    ;;;;; WE WILL LOOK two COLUMNs BEHIND COLUMN
;;;;; TO SEE WHAT OBJECTS NEED TO BE LOADED.
    LDA columnTracker
	sec
    sbc #$02
    AND #%00001111
    STA scroll_hold
    
   ; LDA columnTracker
  ;  AND #%00001111
  ;  CMP #%00001111
  ;  BNE gotColumnTrackerUpdate
  ;  DEC xScroll_hi
  ;  JMP gotColumnTrackerUpdate
gotColumnTrackerUpdate:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;; NORMALIZE COLUMN TRACKER
    LDA columnTracker
    AND #%00011111
    STA columnTracker
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;CHECK BIT 5 to see if we need to change nametable
skipIncDecNT:
    LDA columnTracker
    AND #%00010000
    BEQ showingNTisZero
    LDA showingNametable
    BNE skipSettingNewNT
    LDA #$01
    JMP gotShowingNT
showingNTisZero:
    LDA showingNametable
    BEQ skipSettingNewNT
    LDA #$00
gotShowingNT:
	
    STA showingNametable
    LDA scrollDirection
    BEQ DecreaseNametables
  
    INC currentNametable
    INC leftNametable
    INC rightNametable
   

    
    JMP setNewNT
DecreaseNametables:
	
    DEC currentNametable
    DEC leftNametable
    DEC rightNametable

    
setNewNT: 
  ; LDA currentNametable
   ; STA temp;
   LDA #$01
	STA scrollRoomDataFlag
	;JSR LoadScrollRoomData
    LDA currentNametable
    STA temp;
   
dontUpdateScrollRoomData:

skipSettingNewNT:
    ;LDA columnTracker
    ;AND #%00001111
    ;BNE dontUpdateScrollRoomData


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;NAMETABLE IS NOW ASSIGNED.
;;;;;;;; TIME TO UPDATE DATA -
        ;; TILES
        ;; ATTRIBUTES
        ;; COLLISION
	;;;;;;;;;;;;;;;;;;;;;;;;; We need to check to see if there is a hud.
	;;;;;;;;;;;;;;;;;;;;;;;;; if there is a hud, we need to check the height of the hud.
	;;;;;;;;;;;;;;;;;;;;;;;;; Height of hud = how many tiles the hud takes up.
	;;;;;;;;;;;;;;;;;;;;;;;;; So #$0f - that number = how many tiles to write,
	;;;;;;;;;;;;;;;;;;;;;;;;; and also gives us a starting offset of where to draw tiles.
	

    LDA #$0f
	sec
	SBC #BOX_0_HEIGHT
	STA tilesToWrite ;; how many tiles total will we write, even though we can only write four per frame 
    LDA #$00
	
    STA OverwriteNT_row
    STA rowTracker
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;;;;;;;;;;;;;;;;;;;;;;;GET NAMETABLE TO PULL FROM
	
    LDA scrollDirection
    BNE updateNametablesRight
    LDA columnTracker

    AND #%00001000
    BNE ++
    LdA leftNametable
    JMP +
++
    LDA leftNametable
    clc
    adc #$01
+
    STA newNametable
    JMP DoneGettingNTupdate
    
    

updateNametablesRight
	
    LDA columnTracker
    AND #%00001000
    BNE ++
    LdA rightNametable
    JMP +
++
    LDA rightNametable
    CLC
    ADC #$01
+
    STA newNametable
DoneGettingNTupdate:
    LDA #$01
    STA loadingTilesFlag
    ;;; These things only happen once.
    JSR doUpdateAttributeColumn
    JSR doUpdateCollisionColumn
	
	LDA scrollRoomDataFlag
	BEQ skipUpdateToNewNametable

	JSR LoadScrollRoomData
	LDA #$00
	STA scrollRoomDataFlag
skipUpdateToNewNametable:	
doScrollTileUpdate:


doneWithScrolling:
    LDA loadMonsterColumnFlag
    BNE timeToCheckLoadMonsters
    JMP notTimeToLoadMonsters
timeToCheckLoadMonsters:
    ;; time to load monsters
    CheckScrollColumnForNewMonsters #$04
notTimeToLoadMonsters:
    ;;; reload the player in case x was corrupted.
    ;;; this only ever happens in the player object
    ;;; so we it could have only gotten to this point
    ;;; if x was the player object, so return it to that.
    LDX player1_object
skipScroll_notPlayer:
    RTS
    
    
doScrollTileUpdateOverflow
    TXA
    PHA
  ;  JSR doUpdateTileColumn
    PLA
    TAX
    RTS
    
    
    
    
    
    
    
doUpdateCollisionColumn:
    LDA currentBank
    STA prevBank
    LDY #$16 ;; screen bank
    JSR bankswitchY
    
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    LDY newNametable
	LdA update_screen_details
	CMP #$01
	;;; if it is zero, it is a special screen.
	;;; one is map 1
	;;; two is map 2
	BNE +
    LDA CollisionTables_Map1_Lo,y
    STA collisionPointer
    LDA CollisionTables_Map1_Hi,y
    STA collisionPointer+1
	JMP ++
+
   LDA CollisionTables_Map2_Lo,y
    STA collisionPointer
    LDA CollisionTables_Map2_Hi,y
    STA collisionPointer+1
++
    
    LDA #$0f
    STA updateCol_columnCounter

    
;   LDA columnTracker
;   AND #%00000001
;   BNE skipDoingColColumnTracking ;;; only do if even
    LDA columnTracker
    CLC
    ADC #$18
    STA temp
    LDA scrollDirection
    BEQ left_getRightOfScreenSeam
    ;; get left of screen seam

    JMP gotScrollHold ;; where to update objects
left_getRightOfScreenSeam:  

gotScrollHold:
    LDA temp
    AND #%00011111
    STA screenSeam
    AND #%00010000
    BNE oneForUpdateColTable
    LDA #$00
    JMP gotUpdateColTable
oneForUpdateColTable:
    LDA #$01
gotUpdateColTable:
    STA updateCol_table
    
    LDY screenBank ;; screen bank
    JSR bankswitchY
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; WRITE TO RIGHT NAMETABLE COLLISION 
    LDA columnTracker
    CLC
    ADC #$18
    AND #%00001111
    TAx
    LSR
    TAy
doUpdateColColLoop:
;;;;;;;;;;;;;
    LDA columnTracker
    AND #%00000001
    BNE loadOddCol
    JSR LoadEvenCollisionPoint
    JMP colLoaded
loadOddCol:
    JSR LoadOddCollisionPoint
colLoaded:
    INX
    DEC updateCol_columnCounter
    LDA updateCol_columnCounter
    BEQ doneWithColColumnUpdate
    ;;;;;;;;;;;;; more to go
    
    TXA
    CLC
    ADC #$0f
    TAX
    TYA
    CLC
    ADC #$08
    TAY
    
    JMP doUpdateColColLoop
doneWithColColumnUpdate:


    LDY prevBank
    JSR bankswitchY
    
    

    RTS
    
    
    
    
    
    
    
    

doUpdateAttributeColumn:

    LDX #$00
    ;;; ok, so we need *half* of the column tracker, since ever attribute takes up two columns.

    LDA columnTracker
    AND #%00111111
    LSR
    CLC
    ADC #$0c
    AND #%00001111
    TAY
    LDA attrColumnTableLo,y
    STA temp
doAttributeColumnLoop:
    LDA temp
    STA updateNT_att_fire_Address_lo,x
    LDA attrColumnTableHi,y
    STA updateNT_att_fire_Address_hi,x
    LDA temp
    CLC
    ADC #$08
    STA temp
    INX
    CPX #$08
    BNE doAttributeColumnLoop


    LDA currentBank
    STA prevBank
    LDY #$16
    JSR bankswitchY

    LDY newNametable
	LdA update_screen_details
	CMP #$01
	;;; if it is zero, it is a special screen.
	;;; one is map 1
	;;; two is map 2
	BNE +
	LDA AttributeTablesMainGameAboveLo,y
	STA temp16
	LDA AttributeTablesMainGameAboveHi,y
	STA temp16+1
	JMP ++
+
	LDA AttributeTablesMainGameBelowLo,y
	STA temp16
	LDA AttributeTablesMainGameBelowHi,y
	STA temp16+1
++
    LDY screenBank ;;; screen bank
    JSR bankswitchY
    
    
    ;LDA #%00000000
    ;STA updateNT_attMask ;; update whole byte, replace ALL values
    LDX #$00
    ;LSR ;; half since attributes have half the number of values.
    LDA columnTracker

    AND #%00011111
    LSR
    CLC
    ADC #$0c
    AND #%00000111
    TAY
DoAttributesPullLoop:
    
    LDA (temp16),y
    STA updateNT_fire_Att,x
    TYA
    CLC
    ADC #$08
    TAY
    INX
    CPX #$08
    BNE DoAttributesPullLoop
    LDA #$00
	STA updateNT_attMask
    LDY prevBank
    JSR bankswitchY

    RTS
    

    
    
doUpdateTileColumn:
    RTS
 
    
    
    
    
LoadScrollRoomData:
;;;;; DO ALL UPDATES THAT HAPPEN IN THE "CURRENT NAMETABLE"
	LDA currentNametable
	STA temp;
	JSR GetCollisionMap

    LDA #SCREEN_DATA_OFFSET
    CLC
    ADC #$3E
    TAY
    LDA (collisionPointer),y
    STA screenFlags
	
	LDY #SCREEN_DATA_OFFSET
	LDA (collisionPointer),y
	STA screenType

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Slightly complicated explanation here.
;;;; We may have it where if we destroy all monsters,
;;;; scrolling can resume (there is a bit for that).
;;;; But what if we're not fully loaded onto a particular screen
;;;; when we shoot the last monster and that bit check takes place?
;;;; Here, we will check that bit, since now the new screenFlags for the
;;;; new screen are loaded, and if it's flipped, we will do a monster count.
;;;; if they are all gone, we will turn on scrolling.
	JSR HandleToggleScrolling

	LDY prevBank
    JSR bankswitchY
;;;;;; DO ALL UPDATES THAT HAPPEN IN THE "NEW NAMETABLE"

    RTS
    
    
    

    
    
LoadMonsterAtColumn:
    
    LDA (collisionPointer),y
    AND #%11110000
    STA temp ;; y value
    LDA (collisionPointer),y
    ASL
    ASL
    ASL
    ASL 
    STA temp1
    CreateObject temp1, temp, ObjectToLoad, #$00, tempNT
    ;;;;;;;;;;;;;; x    y    mon   action   nametable 
    LDA objectID_forSeamLoad
    STA Object_ID,x
    LDA Object_status,x
    ORA #%00000100
    STA Object_status,x
    RTS
    
    
    
    
CheckObjectsForRecursiveLoad:
    ; LDA #$00
    ; STA theFlag
    ; TXA
    ; STA tempObj
    ; LDX #$00
; DoCheckObjColumnLoadLoop
    ; LDA Object_status,x
    ; AND #%10000000
    ; BEQ skipThisObject_passedTest
    ; LDA Object_home_screen,x
    ; CMP tempNT ;; where this object will load
    ; BNE skipThisObject_passedTest
    ; ;;; object x is on,
    ; ;;; object x has same home screen as new potential object.
    ; LDA Object_ID,x
    ; CMP objectID_forSeamLoad ;; temp, in this case, is the id of the object being loaded.
    ; BNE skipThisObject_passedTest
    
    ; LDA #$01
    ; STA theFlag
    ; LDX tempObj
    ; RTS
; skipThisObject_passedTest:
    ; INX
    ; CPX #TOTAL_MAX_OBJECTS
    ; BNE DoCheckObjColumnLoadLoop
    ; LDA #$00
    ; STA theFlag
    ; LDX tempObj
    
    RTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;





HandleToggleScrolling:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        ;; THE FOLLOWING BIT OF CODE
        ;; CAN MAKE NON-SCROLLING SCREENS
        ;; START TO SCROLL IF ALL MONSTERS
        ;; ARE DEFEATED.
        
        ;; GOOD FOR BOSSES IN PLATFORMERS/SHOOTERS
        ;; AND GREAT FOR BEAT EM UPS.
        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        CountObjects #%00001000, #$01
        ;;; if there was no objects,
        ;;; #$01 is now in the accumulator.
        CMP #$01
        BNE +
   
        LDA screenFlags
		AND #%10111111 ;; flip the zero monsters bit.
						;; which would now allow for scrolling.
		STA screenFlags
     +:
     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;   

	RTS
	
	
	
AlignScreenToNametable:

;; HANDLE ALIGN SCREENS

	LDA align_screen_flag
	BNE dontskipaligningscroll
	LDA #$00
	JMP +++
dontskipaligningscroll:
;;;;;;;;;;======================================

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	

	;; is aligning bottom bounds.
	LDA #$00
	STA gameHandler
	
	LDA align_screen_flag
	AND #%10000000
	BEQ doRightScrollAlign
	;;; do left scroll align.
	LDA xScroll
	sec
	sbc #$04
	BCC doneWithAlign
	STA xScroll
	LDA #$01
	JMP +++
	;JMP gotNewYPos
	
doRightScrollAlign:
	LDA xScroll
	CMP #$04 ;; speed of alignment
	BCC doneWithAlign
	LDA xScroll
	CLC
	ADC #$04
	BCS doneWithAlign
	STA xScroll
	LDA #$01
	JMP +++
	;JMP gotNewYPos
doneWithAlign:
	DEC genericTimer
	BEQ alignTimerIsDone
	LDA #$01
	JMP +++
	;JMP gotNewYPos
alignTimerIsDone:
	LDA #$00
	STA prevent_scroll_flag

	LDX player1_object
	LDA Object_scroll,x
	STA nt_hold
	AND #%00000001
	STA showingNametable

	LDA #$00
	STA xScroll
	STA yScroll
	
	LDA align_screen_flag
	AND #%00000011
	CMP #$01
	BNE +
	LDA #$00
	STA align_screen_flag
	JSR doBottomBounds_player
	LDX tempx
	LDA #$01
	JMP +++
	;JMP gotNewYPos
+
	LDA #$00
	STA align_screen_flag
	JSR doTopBounds_player
	ldx tempx
	;JMP gotNewYPos
	LDA #$01
+++:

	RTS
	
	
	
	
	

	
	
GetCollisionMap:
	LDA currentBank
    STA prevBank
    LDY #$16 ;; screen bank
    JSR bankswitchY
    
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
	;; temp is screen.
    LDY temp
    
	LdA update_screen_details
	CMP #$01
	;;; if it is zero, it is a special screen.
	;;; one is map 1
	;;; two is map 2
	BNE +
    LDA CollisionTables_Map1_Lo,y
    STA collisionPointer
    LDA CollisionTables_Map1_Hi,y
    STA collisionPointer+1
	JMP ++
+
   LDA CollisionTables_Map2_Lo,y
    STA collisionPointer
    LDA CollisionTables_Map2_Hi,y
    STA collisionPointer+1
++
    LDY screenBank
    JSR bankswitchY

	RTS


To anyone reading these posts, I'm mainly using this to journal the problem at this point. Partly as a motivator to keep my eyes on the prize and secondly so I can keep all the seemingly relevant scripts in one place so I can organize my investigation between platforms. Soooooo, yeah, I'm not crazy just rattling off .....yet.
 

Raftronaut

Member
Annnnnnnnnnnnnnnnd that theory is incorrect as well. Adjusting the scroll speed down to 1 and the glitch occurs same as ever, with little difficulty.
https://youtu.be/wEXtA6KgTik

Back to square one I suppose. I tried following Kasumi's directions on his community tutorial on "finding and debugging problems with nesmaker" but have not yet had any luck isolating exactly what is happening whenever the glitch occurs.
 

dale_coop

Moderator
Staff member
Have you tried to set the flag "Edge Stops Player" for all your screen? Maybe it would prevent edge collisions?
 

dale_coop

Moderator
Staff member
Hmmm... you know what... my previous answer made me think I could search for that flag in the scripts to see how it is used.
And I found out that Handle Bounds scripts ARE used in scrolling screens too!

So, maybe you could try modify your DoLeftBoundsUpdate and DoRightBoundsUpdate scripts...
Adding a code to kill your player (for example):
Code:
	JSR HandlePlayerDeath
	PlaySound #SND_HURT_PLAYER
	JMP doneWithUpdateLeftScreen
Just after the "+" line.
Now, when your player touches the edge he should be killed.
 

Mugi

Member
sounds like something that could work, but seeing the gameplay of spaceraft, i wonder if instantly dying when touching the edge is a good solution.
the game feels more like it should only kill the player when it gets "pressed" between the edge and a solid object.

one way to handle that would be to add some extra checks to the bound handler so that when you're touching the left edge, if the playerobject's right collision points register a collision, only then it would kill the player.

that still leaves out the problem of getting stuck on the edge though. Im not sure why the fix i provided for that doesnt work for him, but it propably has something to do with physics at this point because the edge handlers i modified are identical
between the shooter module and the platformer module, and while it works when using the platformer physics, it obviously messes up with the non-gravity one.
 

Raftronaut

Member
Mugi said:
sounds like something that could work, but seeing the gameplay of spaceraft, i wonder if instantly dying when touching the edge is a good solution.
the game feels more like it should only kill the player when it gets "pressed" between the edge and a solid object.

one way to handle that would be to add some extra checks to the bound handler so that when you're touching the left edge, if the playerobject's right collision points register a collision, only then it would kill the player.

that still leaves out the problem of getting stuck on the edge though. Im not sure why the fix i provided for that doesnt work for him, but it propably has something to do with physics at this point because the edge handlers i modified are identical
between the shooter module and the platformer module, and while it works when using the platformer physics, it obviously messes up with the non-gravity one.

Mugi is right, adding the extra code to destroy player at the screen edge would not be the best solution as far as gameplay at all. but
I was hoping that it would lead me in the direction of understanding the source of the problem at the screen edge.
if memory serves me correctly Mugi, your fix took away all of the player's input control and had some odd teleporting behavior. I went back to that thread maybe two weeks ago and tried it again and got the same results. This time, instead of getting distracted by playing with famitracker I actually decided to re-dedicate myself to learning ASM language so at least I can start to build the knowledge base to understand how to solve these types of problems I come across. It's very challenging for me to be sure, but I am slowly starting to understand some of the concepts and it's getting somewhat easier to navigate. I am looking forward to being over the hump as they say.

I am going to try Dale's code in my bounds handler scripts and test to see what happens. I had tried something similar last night but in my ignorance I neglected to use the JSR command and the jmp done with updates. Anyway, it laughably did not work...
 

Raftronaut

Member
dale_coop said:
Hmmm... you know what... my previous answer made me think I could search for that flag in the scripts to see how it is used.
And I found out that Handle Bounds scripts ARE used in scrolling screens too!

So, maybe you could try modify your DoLeftBoundsUpdate and DoRightBoundsUpdate scripts...
Adding a code to kill your player (for example):
Code:
	JSR HandlePlayerDeath
	PlaySound #SND_HURT_PLAYER
	JMP doneWithUpdateLeftScreen
Just after the "+" line.
Now, when your player touches the edge he should be killed.

YUP, I made a new script called DoLeftBoundsUpdateKill.asm, in project settings set it to handle-left-bounds with the following edtied script:
Code:
	LDA screenFlags
	AND #%00000010
	BEQ +
	
	;LDA screenFlags
	;AND #%00000100 ;; left bounds autoscrolling right
	;BEQ +
	
	JSR HandlePlayerDeath
	PlaySound #SND_HURT_PLAYER
	JMP doneWithUpdateLeftScreen
	
	LDA #$00
	STA Object_x_lo,x
	STA Object_h_speed_lo,x
	STA Object_h_speed_hi,x
	; for auto scrolling, use xScroll instead of xPrev? 
	;LDA xScroll
	LDA xPrev
	STA xHold_lo
	STA Object_x_hi,x
	STA xHold_hi
;	LDA xScroll_hi
;	STA Object_scroll,x
;	STA nt_hold


	JMP doneWithUpdateLeftScreen
	
+
	
	LDA #BOUNDS_RIGHT
	SEC
	SBC Object_right,x
	SEC 
	SBC #$02

	STA xHold_hi
	STA newX
	STA Object_x_hi,x
	
	LDA Object_y_hi,x
	STA newY
	
		LDA #$00
	STA xHold_lo
	STA yHold_lo

;;; check if side triggers change screen...for instance, if hurt, wouldn't, and would instead return *solid*

	
	LDA Object_scroll,x
			sec
			sbc #$01
			STA Object_scroll,x
			STA currentNametable
			STA newScreen
			STA currentScreen ;currentScreen
			STA xScroll_hi
			
			clc
			adc #$01
			STA rightNametable
			SEC
			SBC #$02
			STA leftNametable
			
			LDA Object_scroll,x
			AND #%00000001
			STA showingNametable
			
			
			LDA #$01
			STA screen_transition_type
			
		;LDA warpMap
			;CLC
			;ADC #$01
			;STA update_screen_details
			
			;;; update screen details will not change with left, right, up or down movement
			;;; unless the edge of a map should take you to the other map or something.
			;;; warpMap variable is used solely to hold whether the warp-out is to overworld or underworld.
			

		
	LDA #$01
	STA tile_solidity		
	LDA #$00
	STA gameHandler
		LDA #%11000000
				;7 = active
				;6 = 8 or 16 px tiles
			ORA #GS_MainGame
			ORA #%01000000
			STA update_screen

doneWithUpdateLeftScreen:
It seems to work as designed! It destroys the player at the left screen edge seemingly without fail no matter which collisions or input commands are being pressed. This is certainly not a perfect fix, but the game functions without breaking for the moment, so that is theoretically a BIG step forward.
I did not bother to update HandleRightBounds since I have never managed to trigger the bug on the right screen edge. I tried to compare the two (left and right) scripts but am not yet knowledgeable enough to spot any potential errors.

Here is the DoRightBoundsUpdate.asm script for ease of reference:
Code:
	LDA screenFlags
	AND #%00000010
	BEQ +
	
	;LDA screenFlags
	;AND #%00000100 ;; left bounds autoscrolling right
	;BEQ +
	
	JSR HandlePlayerDeath
	PlaySound #SND_HURT_PLAYER
	JMP doneWithUpdateLeftScreen
	
	LDA #$00
	STA Object_x_lo,x
	STA Object_h_speed_lo,x
	STA Object_h_speed_hi,x
	; for auto scrolling, use xScroll instead of xPrev? 
	;LDA xScroll
	LDA xPrev
	STA xHold_lo
	STA Object_x_hi,x
	STA xHold_hi
;	LDA xScroll_hi
;	STA Object_scroll,x
;	STA nt_hold


	JMP doneWithUpdateLeftScreen
	
+
	
	LDA #BOUNDS_RIGHT
	SEC
	SBC Object_right,x
	SEC 
	SBC #$02

	STA xHold_hi
	STA newX
	STA Object_x_hi,x
	
	LDA Object_y_hi,x
	STA newY
	
		LDA #$00
	STA xHold_lo
	STA yHold_lo

;;; check if side triggers change screen...for instance, if hurt, wouldn't, and would instead return *solid*

	
	LDA Object_scroll,x
			sec
			sbc #$01
			STA Object_scroll,x
			STA currentNametable
			STA newScreen
			STA currentScreen ;currentScreen
			STA xScroll_hi
			
			clc
			adc #$01
			STA rightNametable
			SEC
			SBC #$02
			STA leftNametable
			
			LDA Object_scroll,x
			AND #%00000001
			STA showingNametable
			
			
			LDA #$01
			STA screen_transition_type
			
		;LDA warpMap
			;CLC
			;ADC #$01
			;STA update_screen_details
			
			;;; update screen details will not change with left, right, up or down movement
			;;; unless the edge of a map should take you to the other map or something.
			;;; warpMap variable is used solely to hold whether the warp-out is to overworld or underworld.
			

		
	LDA #$01
	STA tile_solidity		
	LDA #$00
	STA gameHandler
		LDA #%11000000
				;7 = active
				;6 = 8 or 16 px tiles
			ORA #GS_MainGame
			ORA #%01000000
			STA update_screen

doneWithUpdateLeftScreen:

I am still not any closer to understanding what is happening at the screen edge yet, clearly there is a value that is overflowing somewhere. I tried walking through Kasumi's debugging with Mesen tutorial and am still not quite there yet......

thanks as always for the assist Dale :)
 

Mugi

Member
this is a really stupid question, but are your screen edges set to solid (on all screens) when you tried the files i gave you.
they shouldn't do any teleporting anywhere whatsoever, because all that fix does, is changes the way the game reads the collision with the edge to make it not ignore certain conditions.
if you get "teleported" or otherwise, it might just be because the edge is not actually blocking you as it should.
 

Raftronaut

Member
OH YES , I should have clarified after Dale asked. Yes, all of my screens checks the "edge stops player" box with the exception of the last screen in a row, where I have the screen edge set to allow the player to pass through to the other side to continue on to the next section.

I've noticed, that at these final screens of a row when I uncheck this flag, the scrolling stops and it allows my player to move left into the previous screen which causes an error. I would ideally like to prevent the player from ever crossing the left screen edge threshold since scrolling shooters do not normally allow for back tracking.
Adding Dale's script to the left bounds handler had the added effect of preventing the player from being able to trigger this as well.

I flashed a cart and tested Space Raft on my AVS last night after adding Dale's script to the left bounds. I played all the way through without triggering the screen edge glitch once. Getting closer! I can taste it now!
 

Raftronaut

Member
ANNND I spoke to soon again, At the end of the first row of screens I have "edge stops player" unchecked to allow the player to pass through as stated above, However, Dale's script is not activated when "edge stops player" is unchecked allowing the player to cross backwards into the previous autoscroll screen as seen here:
https://youtu.be/mCT6MkckCOc

Ideally the left edge would stop the player in scrolling mode or not as I would never want the player to be able to cross that screen edge. This will also be an important detail to anyone making an auto scrolling shooter with nesmaker in the future. The left bounds edge seems to apply universally to nes shooters with the exception of Section Z and Talespin, which are almost a different genre...
 

dale_coop

Moderator
Staff member
If I understand well, for the left bound edge collision, you want to kill the Player when the screen is set to "edge stops player" and just block/stop him when the "edge stops player" is NOT set?
This rule is true for every screen of your game?

So the script would sometjing like:

Code:
	LDA screenFlags
	AND #%00000010
	BEQ +
	;; when the "edge stops player" is set: 
	
	JSR HandlePlayerDeath
	PlaySound #SND_HURT_PLAYER
	JMP doneWithUpdateLeftScreen
	
+

	;; when the "edge stops player" is NOT set: 
	LDA #$00
	STA Object_x_lo,x
	STA Object_h_speed_lo,x
	STA Object_h_speed_hi,x
	; for auto scrolling, use xScroll instead of xPrev? 
	;LDA xScroll
	LDA xPrev
	STA xHold_lo
	STA Object_x_hi,x
	STA xHold_hi


doneWithUpdateLeftScreen:
 

Mugi

Member
here's another idea for you though.
since if this glitch only occurs when you actually touch the edge, you could simply make your input script not allow you to touch it.

i use this code on my wall grab tile to prevent wallgrab from ever happening on the top 16 pixels and bottom 16 pixels of the screen.

Code:
; Bounds checker to not tilt the game when wallgrabbing on nametable edge.
; not used on 4-way scroller.

    ; CLC
    ; ADC Object_y_hi,x           ; are we in map bounds ?
    ; CMP #$10
    ; BCC dontMessWithGravity
    ; CMP #$D6
    ; BCS dontMessWithGravity

as imilar check could be implemented into your "move left" script that checks if you're in the first.... say 4 pixels of the screen, you could not move left anymore, thus far never touching the edge.

for a check against left, it would be something along the lines of:

Code:
LDX player1_object
LDA Object_h_speed_hi,x
CLC
ADC Object_x_hi,x
CMP #$08
BCC + ; try BCS instead of BCC if this doesnt work, i always mix this stuff up lol
RTS

tack that to the beginning of your "move left" script and it should disallow you to ever move to the first 8 pixels of the left edge of a screen.

(im not sure but you might have to factor in scroll for this and deal with the xscroll offsetting of nametables, but the basic idea is the same.)
this also would have the added benefit of that with this, you could just leave the edge code in place to kill the player when the edge is touched.
that way you could never touch it by driving into the edge, but if a solid object would push you, it would still push you all the way to the edge and then kill you by touching it.
 
Top Bottom