Bucket Mouse
Active member
In a recent video Joe Granato showed how the new build of NESMaker will have the power to store text data in any bank with free space. But there is a way to do this in the current build (4.1.5), it's just a bit more complicated.
https://www.youtube.com/watch?v=dQHdSI3fEEE&feature=youtu.be
I originally posted this in the "Bank Boomerangs" topic, which uses a related trick, but Dale said it was too different to be appropriate for that topic, so I'm posting it here as a separate resource.
HOW TO STORE TEXT IN OTHER BANKS
First make the variable bank18switch in User Variables.
Then copy all this into Notepad and save it as HandleTextBox2.asm in Routines/nameofyourproject/System, next to the original HandleTextBox:
Go back to Project Settings > Script Settings and add a new Script Define:
Name: Handle Text Box 2
Define: SCR_HANDLE_TEXTBOX2
Click OK and then, while your new Script Define is highlighted, use the Visible ASM Files window next to it to navigate to the place you stored HandleTextBox2, and double click. Now it's registered there.
Now find the BankData folder, open up Bank18.asm and add these things:
Those are the first five entries in npcText2.dat. I suggest you add more as you're writing the dialogue you're using it for, because it would be tedious to type it all out at once (but if you want to try, be my guest).
Almost done. Look in the System folder for HandleBoxes.asm. Find the bit of code that starts with "NotChangingAttributes." Directly below it and above LDA currentBank, paste this in:
You're ready. Now here's how to use it. npcText2 starts with text entry 256, but you're fooling the script into thinking it's text entry 0. It will likewise think text entry 257 is 1, 258 is 2, and on. To keep this from becoming confusing, take the text entries you want, subtract 256 from 'em, and select those numbers in Text Groups on any screen in which you're using the bank switch.
As for how to trip the switch, that's the tricky part. You cannot trip it on a screen that is already loaded. Tiles, monster AI and mapping it to a button will not work! The only place you can shift text banks is during the screen loading process....specifically, HandleScreenLoads.asm.
Foertunately, you really only need to use this once, if ever. If you find yourself at a point where you've run out of text, take a look at the overworld grid and make a note of the specific screen on which you want to shift banks. It should be in hex (0 through 9 and then A through F), so for example, the rightmost screen on the top row would be #$0F.
Then find System > HandleScreenLoads.asm and modify the top to look like this:
And that should do it.
https://www.youtube.com/watch?v=dQHdSI3fEEE&feature=youtu.be
I originally posted this in the "Bank Boomerangs" topic, which uses a related trick, but Dale said it was too different to be appropriate for that topic, so I'm posting it here as a separate resource.
HOW TO STORE TEXT IN OTHER BANKS
First make the variable bank18switch in User Variables.
Then copy all this into Notepad and save it as HandleTextBox2.asm in Routines/nameofyourproject/System, next to the original HandleTextBox:
Code:
HandleTextBox2:
;; textboxHandler
; 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0
;7 = Textbox is active.
; 6 = Black box is being created, will then create text.
; 5 = Attribute update to black.
; 4 = text is being created.
; = loop to 5 if more text.
; 3 = Black box is being created, with then restore NT
; 2 = Attributes to main NT
; 1 = restore NT
; 0 = check for "more" text.
LDA textboxHandler
AND #%10000000
BNE textboxIsActive2
RTS ;; textbox is inactive.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
textboxIsActive2:
LDA updateNametable
BEQ notAlreadyWritingToNT2
RTS
notAlreadyWritingToNT2
;HideSprites
;; DO TEXT BOX STUFF.
;; Which text box stuff to do is determined by the textboxHandler byte.
;; if it is active, but all other bits are inactive, that means we have just activated and we need to turn
;; this system on.
LDA textboxHandler
AND #%01111111
BNE textboxSettingsAlreadyOn2
;;; get textbox settings
;; skip resetting the offset.
LDA #$00
STA updateNT_offset
STA updateHUD_offset
STA updateNT_H_offset
STA updateNT_V_offset
;;; zero out the things offsets and start creating the blackout box.
LDA #%11000000
STA textboxHandler ;; flow right into the next.
textboxSettingsAlreadyOn2:
LdA textboxHandler
AND #%01000000
BEQ notCreatingBlackBox2
;;;; CREATE THE BLACK BOX.
;;;; The frist phase is to create the black box.
;;;; no matter what color the text box will be, or which palette it will use
;;;; it will always first create a box of "blanks" so it can be changed to whatever
;;;; background attribute you'd like without noticing the attribute change.
JSR CreateBlackBox2
;;; now the black box has been created.
notCreatingBlackBox2:
LDA textboxHandler
AND #%00100000
BEQ notSettingTextboxAttributes2
JSR isWritingTextboxAttributes2
RTS
;;; set textbox attributes.
notSettingTextboxAttributes2:
LDA textboxHandler
AND #%00010000
BEQ notUpdatingTextboxText2
;; updating textbox text.
JSR isWritingTextToTextbox2
RTS
notUpdatingTextboxText2:
LDA textboxHandler
AND #%00001000
BEQ notErasingTextboxText2
JSR CreateBlackBox2
RTS
notErasingTextboxText2:
LDA textboxHandler
AND #%00000010
BEQ notRestoringNametables22
; JSR CheckForEndOfTextString
; LDA gameHandler
; AND #%00100000
; BEQ notRestoringNametables2 ;; because we have finished.
JSR RestoreNametableData2
notRestoringNametables22
RTS
getUpdateTileOffsetPosition2:
LDA xScroll
LSR
LSR
LSR
LSR
CLC
ADC #BOX_1_ORIGIN_X
CLC
ADC updateNT_H_offset
STA tileX
LDA #BOX_1_ORIGIN_Y
CLC
ADC updateNT_V_offset
STA tileY
JSR coordinatesToMetaNametableValue
;;; spits out updateNT_pos as low and updateNT_pos+1 as hi of address to write.
;;; right now, this is in terms of a single nametable, starting at $20 as the high top left corner high byte.
;;;; FOR HITE BYTE:
;;;; Take columnTracker and divide by 2 so you get a value 0-16
;;;; Add that number to BOX_1_ORIGIN_X.
;;;; If the sum is less than 16, this should stay in the same nametable.
;;;; Otherwise, it should cross nametables.
Ldy columnTracker
LDA columnTracker
AND #%00001111
CLC
ADC #BOX_1_ORIGIN_X ;; plus offset of what tile you're drawing.
CLC
ADC updateNT_H_offset
AND #%00010000
BNE +
;;; same nametable
LDA columnTracker
AND #%00010000
BNE +++
;; this started in even table,
;; so it should stay in even table
JMP updateIsEvenTable2
+++
;;; this started in odd table
;;; so it should stay in odd table.
JMP updateIsOddTable2
+
;;; different nametable
LDA columnTracker
AND #%00010000
BNE +++
;; this started in even table
;; so it should now be odd table.
JMP updateIsOddTable2
+++
;;; this started in odd table
;;; so it should now be even table.
JMP updateIsEvenTable2
updateIsEvenTable2:
LDA updateNT_pos+1
AND #%00000011 ;; 0,1,2 or 3
clc
adc #$20
STA updateNT_pos+1
JMP gotHiUpdatePos2
updateIsOddTable2:
LDA updateNT_pos+1
AND #%00000011 ;; 0,1,2 or 3
CLC
ADC #$24
STA updateNT_pos+1
JMP gotHiUpdatePos2
gotHiUpdatePos2:
RTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
checkNTforNewTile2:
LDA temp3; updateNT_pos
AND #%00100000
CMP #%00100000
BEQ newTileHasCrossedThreshold2
;; new tile has not crossed threshold.
;; same nametable
LDA columnTracker
AND #%00010000
BNE updateIsOddTable2
JMP updateIsEvenTable2
newTileHasCrossedThreshold2:
LDA columnTracker
AND #%00010000
BNE updateIsEvenTable2
JMP updateIsOddTable2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;RTS ;; redundant, unnecessary
isWritingTextToTextbox2:
;;; already in bank 17.
;; we need to get positioning.
LDA xScroll
LSR
LSR
LSR
STA temp
LDA #BOX_1_ORIGIN_X
ASL
CLC
ADC updateNT_H_offset
CLC
ADC temp
STA temp3
AND #%00011111
STA tileX
LDA #BOX_1_ORIGIN_Y
ASL
CLC
ADC updateNT_V_offset
STA tileY
JSR coordinatesToNametableValue
JSR checkNTforNewTile2
;; temp has position value.
;LDA temp
;STA updateNT_pos
LDY textVar ;; what string
LDA screenText,y
TAY
LDA stringsTableLo2,y
STA temp16
LDA stringsTableHi2,y
STA temp16+1
LDY textboxOffsetHold
LDA (temp16),y
;; this is where we determine if this is a special character or a normal letter/number to update.
CMP #_END
BNE +
;;;; END:
EndText2:
;; this is and _END value.
;; it turns off writing to the textbox.
LDA textboxHandler
AND #%01111111
STA textboxHandler
;;;;; textbox handler stays on the same state
;;;;; but deactivates.
;;;;; it will start again if bbutton is pressed again
;;;;; on state 00001000, which will begin the 'turn off' process.
JMP doneTextUpdate2
+
CMP #$FE ;; is it a new line?
BNE notANewLine_text2
;;;; NEW LINE:
LDA #$00
STA updateNT_H_offset
INC updateNT_V_offset
INC textboxOffsetHold
JMP doneTextUpdate2
notANewLine_text2:
CMP #_ENDTRIGGER
BNE notEndTrigger2
;; is an end trigger
INC textboxOffsetHold ;; get the very next value.
LDY textboxOffsetHold
LDA (temp16),y
STA temp
;;;; this now has the trigger to change.
TriggerScreen temp
JMP EndText2
notEndTrigger2:
CMP #_ENDITEM
BNE notEndItem2
;;; Give Item triggers the warp variable.
;; end of the tile script
JMP EndText2
notEndItem2:
CMP #_MORE
BNE notMoreText2
LDA #%10000000
STA textboxHandler
;; flip a more text flag.
LDA #$01
STA moreText
INC textboxOffsetHold
JMP EndText2
notMoreText2:
;;;; NORMAL VALUE
;;;;; The look up was a normal letter, number, or other hud value.
CLC
ADC #$C0
STA updateHUD_fire_Tile
LDA updateNT_pos
STA updateHUD_fire_Address_Lo
LDA updateNT_pos+1
STA updateHUD_fire_Address_Hi
INC updateNT_H_offset
INC textboxOffsetHold
doneTextUpdate2:
RTS
CreateBlackBox2
LDX #$00
;;; DRAW METATILE 1
;; this macro SETS it to change on the next vblank update.
;; starting at address hi-lo, with the tile in the third argument.
;; if it sees #BLANK_TILE, it will create a meta tile of four blank tiles.
;; if it sees any other value, it will create a metatile starting with
;; that index as the top left corner.
;;;;;;; IF THE GAME DOES NOT SCROLL:
;;;;;;; You can simply read the box value, do a little math, to get the offset
;;;;;;; of the address for the top left corner, and skip the whole business of
;;;;;;; finding the offset based on the scroll.
;;;;;;; IF THE GAME DOES SCROLL:
;;;;;;; YOu will need to get the offset value so it is at the box value position
;;;;;;; compared to the CAMERA rather than zero.
;;;;;;; Fortunately, we already have tables set up for columnTableLo and columnTableHi
;;;;;;; which we use to handle the column for scrolling.
;;;;;;; we can use the columnTracker value to help determine the proper offset.
;;;;;;; There can only be 32 columns.
;;;;;;; And we will use updateNT_offset to continue this over frames,
;;;;;;; and to handle tile to offset.
;;;;;;; this will be set to zero wherever HandleTextBox is activated.
;;;;;;; It needs to, in some way, be boolean (like a key press, or using a boolean var).
; LDA #$00
; STA updateNT_H_offset
; STA updateNT_V_offset
;; THESE ALSO MUST BE SET IN THE BOOL VAR PLACE
;; Whatever code is calling this must also set these to zero.
LDA #$00
STA tilesToWrite
LDA #$04
STA dummyVar2
DoLoopThing2:
JSR getUpdateTileOffsetPosition2
SetMetaTileToChange updateNT_pos+1, updateNT_pos, #BLANK_TILE
INC updateNT_H_offset
LDA updateNT_H_offset
CMP #BOX_1_WIDTH
BEQ ++
DEC dummyVar2
LDA dummyVar2
BEQ +
JMP DoLoopThing2
++ ;; width has been reached.
;; now to test the height, and to move h pos back to the left.
BEQ dontEndCreatingBlackBox2
JMP EndCreatingBlackBox2
dontEndCreatingBlackBox2:
LDA #$00
STA updateNT_offset
STA updateHUD_offset
STA updateNT_H_offset
INC updateNT_V_offset
+
LDA #$01
STA updateNametable ;; turn on write.
LDA updateNT_V_offset
CMP #BOX_1_HEIGHT
BCS turnOffCreatingTextBox2
LDA updateNT_H_offset
CMP #BOX_1_WIDTH
BEQ turnOffCreatingTextBox2
JMP EndCreatingBlackBox2
turnOffCreatingTextBox2:
;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Setup text specifics
LDA #$00
STA stringGroupOffset
STA updateNT_H_offset
STA updateNT_V_offset
LDA xScroll
LSR
LSR
LSR
STA temp
LDA #BOX_1_ORIGIN_X
ASL
CLC
ADC temp
CLC
ADC #H_PAD_TEXTBOX
STA tileX
LDA #BOX_1_ORIGIN_Y
ASL
CLC
ADC #V_PAD_TEXTBOX
STA tileY
; LDA #$01
; STA writingText ;; turn on writing text.
LDA moreText
BEQ +
LDA #$00
STA moreText
JMP ++
+
LDA #$00
STA textboxOffsetHold
++
STA updateNT_H_offset
STA updateNT_V_offset
LDA textboxHandler
AND #%00001000
BEQ continueToUpdatingAttributes2
;;; is turning textbox OFF, so no writing to text.
;;; will have to allot for changing attributes back with the
;;; bit 00000100
LDA #%10101000 ;; turn on attribute update
STA textboxHandler
LDA #$00
STA updateNT_H_offset
STA updateNT_V_offset
STA updateNT_offset
STA updateNT_compensation
; LDA xScroll_hi
; AND #%00000001
; BEQ oddToEven2
; LDA #$27
; JMP gotNtDeets2
;oddToEven2:
; LDA #$23
;gotNtDeets2:
; STA updateNT_tableLeft
; STA updateNT_details
;LDA columnTracker
LDA #BOX_1_ORIGIN_X
LSR
TAY
LDA attrColumnTableHi,y
STA updateNT_tableLeft
STA updateNT_details
RTS
continueToUpdatingAttributes2:
LDA #%10001000
STA textboxHandler
LDA #$00
STA updateNT_H_offset
STA updateNT_V_offset
STA updateNT_offset
STA updateNT_compensation
; LDA xScroll_hi
; AND #%00000001
; BEQ oddToEven
; LDA #$27
; JMP gotNtDeets:
;oddToEven:
; LDA #$23
;gotNtDeets:
; STA updateNT_tableLeft
; STA updateNT_details
;LDA columnTracker
LDA updateNT_H_offset
CLC
ADC updateNT_offset
ASL
CLC
ADC columnTracker
AND #%00011111
LSR
TAY
LDA attrColumnTableHi,y
STA updateNT_details
LDA #%10100000
STA textboxHandler
;;;;;;;;;;;;;;;;;;;;;;;;;;;
RTS
EndCreatingBlackBox2:
LDA #$01
RTS
RestoreNametableData2:
;;; FIRST we need to find the metaNametale value from the ROM.
;;;;;;;;;;LESSER PRIORITY;;;;;;;;;;;;;;
;;; THEN we need to check it againt collision type to / screen state to see if it should be
;;; change (for instance, a tile that changes at night / saved / changes if no monsters, if there are no monsters, etc)
;;; THEN, we should probably always just restore the hud at the end.
;;; the problem is, the data we need to fetch is in bank 16, then screen bank,
;;; while we are currently in bank 17 with this routine.
;;; so restoration of nametable, or at least analysis of whate tiles to write, will have to happen OUTSIDE of this routine
;;; We handle it in HandleBoxes, which will populate updateTile00-03, and respect paths.
;;; And we handle POSITION to update here.
LDX #$00
LDA #$00
STA tilesToWrite
JSR getUpdateTileOffsetPosition2
LDA updateNT_pos
STA temp
LDA updateNT_pos+1
STA temp1
SetTileToChange temp1, temp, updateTile_00
LDA temp
CLC
ADC #$01
STA temp2
LDA temp1
ADC #$00
STA temp3
SetTileToChange temp3, temp2, updateTile_01
LDA temp
CLC
ADC #$20
STA temp2
LDA temp1
ADC #$00
STA temp3
SetTileToChange temp3, temp2, updateTile_02
LDA temp
CLC
ADC #$21
STA temp2
LDA temp1
ADC #$00
STA temp3
SetTileToChange temp3, temp2, updateTile_03
INC tilesToWrite
LDA #$01
STA updateNametable
INC updateNT_H_offset
LDA updateNT_H_offset
CMP #BOX_1_WIDTH
BNE dontReturnToGame2
LDA #$00
STA updateNT_H_offset
INC updateNT_V_offset
LDA updateNT_V_offset
CMP #BOX_1_HEIGHT
BNE dontReturnToGame2
LDA #$00
STA textboxHandler
STA updateHUD_offset
LDA gameHandler
AND #%11011111
STA gameHandler
;ShowSprites
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; check to see if there is behavior after a text box.
;;;; THIS WOULD WARP YOU TO A SCREEN AFTER TEXTBOX.
; LDA #$01
; STA activateWarpFlag
; PlaySound #SND_ENTER
; LDX player1_object
; LDA Object_x_hi,x
; STA mapPosX
; LDA Object_y_hi,x
; STA mapPosY
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
dontReturnToGame2:
RTS
isWritingTextboxAttributes2:
;; handle all four quadrants of the attribute.
RTS
Go back to Project Settings > Script Settings and add a new Script Define:
Name: Handle Text Box 2
Define: SCR_HANDLE_TEXTBOX2
Click OK and then, while your new Script Define is highlighted, use the Visible ASM Files window next to it to navigate to the place you stored HandleTextBox2, and double click. Now it's registered there.
Now find the BankData folder, open up Bank18.asm and add these things:
Code:
.include SCR_HANDLE_TEXTBOX2
.include "ScreenData\npcText02.dat"
stringsTableLo2:
.db <Text256, <Text257, <Text258, <Text259, <Text260, <Text261
stringsTableHi2:
.db >Text256, >Text257, >Text258, >Text259, >Text260, >Text261
Those are the first five entries in npcText2.dat. I suggest you add more as you're writing the dialogue you're using it for, because it would be tedious to type it all out at once (but if you want to try, be my guest).
Almost done. Look in the System folder for HandleBoxes.asm. Find the bit of code that starts with "NotChangingAttributes." Directly below it and above LDA currentBank, paste this in:
Code:
LDA bank18switch
CMP #$01
BNE bank17switch
LDA currentBank
STA prevBank
LDY #$18
JSR bankswitchY
JSR HandleTextBox2
LDY prevBank
JSR bankswitchY
RTS
bank17switch:
You're ready. Now here's how to use it. npcText2 starts with text entry 256, but you're fooling the script into thinking it's text entry 0. It will likewise think text entry 257 is 1, 258 is 2, and on. To keep this from becoming confusing, take the text entries you want, subtract 256 from 'em, and select those numbers in Text Groups on any screen in which you're using the bank switch.
As for how to trip the switch, that's the tricky part. You cannot trip it on a screen that is already loaded. Tiles, monster AI and mapping it to a button will not work! The only place you can shift text banks is during the screen loading process....specifically, HandleScreenLoads.asm.
Foertunately, you really only need to use this once, if ever. If you find yourself at a point where you've run out of text, take a look at the overworld grid and make a note of the specific screen on which you want to shift banks. It should be in hex (0 through 9 and then A through F), so for example, the rightmost screen on the top row would be #$0F.
Then find System > HandleScreenLoads.asm and modify the top to look like this:
Code:
HandleScreenLoads:
LDA #$ ;;;; insert screen number here
CMP currentScreen
BNE HandleScreenLoads2
LDA #$01
STA bank18switch
HandleScreenLoads2:
And that should do it.