[4.5.9] How To Activate The Night & Day Feature

Bucket Mouse

Active member
From the very beginning, NESMaker's UI has been designed for a day-night cycle. Two sets of four palettes are assigned to every screen, as well as four possible monster configurations (day, night, day triggered, and night triggered). But even though this stuff is there, you can't actually do anything with it. The game will ignore any commands related to "night" colors or monsters, even though it compiles data for them into the ROM anyway.

The reason it doesn't work is because it's only half-finished. Most of the variables and scripts to run this feature are already in the game -- they're just not hooked up. There is one key ingredient that's missing, but we'll get to it. Follow these directions and you can have a day and night cycle in your Adventure module game within minutes.

DOLOADSCREENDATA

In your Subroutines folder you can find a script called doLoadScreenData.asm. Make a copy of it. Then open the copy and find the line "GetScreenTriggerInfo." Higlight everything from that point up to the line that says "triggeredStateInfoIsLoaded." Then delete all that, and replace it with this:

Code:
GetScreenTriggerInfo:
        JSR doClearAllMonsters
;;;;; MONSTERS
    ;;; constants for banks
 
        
        GetTrigger
    
        ;; result of screenType trigger stored in temp3 and also in accum
        ;; x and y restored.
        BEQ thisScreenIsNotTriggered
            ;; this screen IS triggered
            LDA nightbyte
            CMP #$01
            BNE isTriggeredDay
                ;; triggered and night
                LDA #$03
                STA screenState
                JMP triggeredStateInfoIsLoaded
            isTriggeredDay
    
                ; triggered and day
                LDA #$02
                STA screenState
        
            JMP triggeredStateInfoIsLoaded
        ;;=================================
        thisScreenIsNotTriggered
        
            LDA nightbyte
            CMP #$01
            BNE isNormalDay
            
                ;; normal and night
                LDA #$01
                STA screenState

            isNormalDay:
        
                ; normal and day
                LDA #$00
                STA screenState

        triggeredStateInfoIsLoaded:

LDX screenState ;; activates night text group

Once you've done that, make a variable called "nightbyte." I tried many versions of this without an extra variable, but to no avail. Can't get around it.

Scroll down a bit until you find the line "JSR LoadMonster_1". Right above it, put this in:

Code:
        LDX screenState ;; activates night placements for monsters

Yes, that's all it takes. The script from this point onward is already prepped to handle night monster information. It just needs to be loaded with the correct variable in X. screenState is that variable, and it already exists -- it just wasn't written in. It offers four possibilities:

#$00 = day
#$01 = night
#$02 = day triggered
#$03 = night triggered

The current version of NESMaker uses screenState, but only loads it with #$00 or #$02. #$01 and#$03 wouldn't have done anything until now. We've patched in the extra lines to connect those functions and now your monsters will appear in their Night states and colors when the screen is triggered to do so.

To make sure the music also changes, make THIS alteration near the bottom, right where you see these lines:
LDY #178
LDA (collisionPointer),y

178 is the point where the music data for the day screen is stored. The night screen data is stored at #180, the triggered at #179 and the night + triggered at #181, so:

Code:
    LDA screenState
    CMP #$01
    BEQ +night
    CMP #$02
    BEQ +triggered
    CMP #$03
    BEQ +nighttriggered
    LDY #178 ; normal music
    JMP +
    +night:
    LDY #180 ; night music
    JMP +
    +triggered:
    LDY #179 ; triggered music
    JMP +
    nighttriggered:
    LDY #181 ; night triggered music
    +
    LDA (collisionPointer),y

Now for the background palettes -- they have to change too. You can find doLoadBackgroundPalette.asm in your Subroutines folder. Make a copy of it and open the copy. This is actually sort of a macro, but it's not in the macro folder for some reason. Point is, you can patch in the commands to look up the unused night palettes with this. Delete everything in there and use this instead:

Code:
doLoadBackgroundPalettes:
    ;; This is tied to the macro LoadBackgroundPalettes.
    ;; It uses bank and 16 bit label.
    TXA
    PHA
    TYA
    PHA
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;Get Palette Label based on INDEX
    SwitchBank #$16
        LDY arg0_hold
        LDA GameBckPalLo,y
        STA temp16
        LDA GameBckPalHi,y
        STA temp16+1
 
      
        LDA screenState
        CMP #$00 ; not night
        BEQ +
        CMP #$02 ; not night either
        BEQ +
        LDY #$20 ; to load the night palettes, Y must be shifted forward by 32 (20 in hex)
        JMP +night
        +
        LDY #$20 ; to load the night palettes, Y must be shifted forward by 32 (20 in hex)
        JMP +night
        +


        LDY #$00
        +night
        LDX #$00
        loop_LoadBackgroundPalette:
            LDA (temp16),y
            STA bckPal,x
            INY
            INX
            CPX #$10
            BNE loop_LoadBackgroundPalette
        ;;;; end of loop.
        LDA updateScreenData
        ORA #%00000001 ;; palette
        STA updateScreenData


    ReturnBank
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    PLA
    TAY
    PLA
    TAX
    RTS

Time to attach your modified scripts to your game! It's gonna be a bit different this time. In NESMaker, go to Project Settings > Script Settings, go to the top of the list, find "Load Subroutines" and click Edit. Instead of being editable directly, the addresses for doLoadScreenData.asm and doLoadBackgroundPalette.asm are stored HERE. You will have to directly edit the addresses to redirect them to your modified files...sorry, I'm not the one who designed it this way.

Alternately you could just make a copy of NESMaker entirely and directly edit those two files instead of using copies of them...which for some people might be easier.

All that's needed now is a timer to command the screenState flag to night after a certain period of time, and then back to day. All timer programs are usually placed inside doHandleGameTimer.asm. Search for it and bring it up. Then paste all this into it:

Code:
    LDA gameTimerHi
    BEQ +resethi
    CMP #40 ; this should be exactly half of your total time
    BEQ +hihalfway
    LDA gameTimerLo
    BEQ +resetlo ; if lo number reaches zero, this decreases hi number
    DEC gameTimerLo ; otherwise, it decreases the lo digit then cycles again
    JMP +
    +resetlo:
    DEC gameTimerHi
    LDA #100 ; the low mumber counts this number of frames, then decreases the high number by 1
    STA gameTimerLo
    JMP +
    +resethi
    LDA #80 ; enter the amount of time you want between night and day here
    STA gameTimerHi
    LDA #$00
    STA screenState
   ; LoadBackgroundPalettes newPal
        LDA #$01
    STA updateScreenData
    JMP +
    +hihalfway
    DEC gameTimerHi
    LDA #$01
    STA screenState
   ; LoadBackgroundPalettes newPal
        LDA #$01
    STA updateScreenData
    +

Foertunately doHandleGameTimer has an address that you can change from Script Settings. Do that once you've made the changes to the copy.

HOW TO USE THE TIMER

The timer uses two time-related variables that already exist in NESMaker and are there for your timekeeping pleasure. It's possible that you might be using them already for something else. If this is the case, make up two new variables and use them in place of gameTimerHi and gameTimerLo...or you'll have problems.

A variable on the NES can only count to 255, and 255 frames isn't a very long time for a day-and-night cycle. So we use two, as an "hour" and "minute" hand. The minutes count down, subtract one of the hours and count down a new set of minutes. You get the idea.

gameTimerHi will load itself with the number 80. This means both day and night will last a minute (or precisely one minute and four seconds). If you want the time to last longer than that (you probably do), adjust the designated point in the script to 160 (two minutes) or 240 (three minutes). We're about to hit the 255 limit again, so we can't go higher, but three minutes is longer than you think. An average day in Ocarina of Time, from sunrise to sunset, is two minutes and thirty seconds.

You can also write your own timer script if this one isn't sufficient. By using a third variable, you could make the nights super-long.

IF YOU WANT TIME TO STOP IN SOME AREAS

If you want time to freeze under certain conditions -- like when you visit a town, or when you're in a cutscene -- use a Screen Flag. Put this at the beginning of your timer script:

Code:
    LDA ScreenFlags00 ; change to 01 if your first row of screen flags is taken up
    AND #%01000000 ; the 1 in this row will correspond to the flag you want to use -- change as desired
    BEQ +notflagged
    JMP +
    +notflagged

Then flag the screens you want time to stop in.

IF YOU WANT THE NIGHT TO COME INSTANTANEOUSLY

Once day shifts to night, the change will happen upon your next screen load. But if you want the colors onscreen to instantly change when it hits night, I gave you a way to do that. Just uncomment the two instances of "LoadBackgroundPalettes" in the timer script. Poof, it's night! However the monsters will not change until you visit the next screen.

IF YOU WANT TO COMMAND THE NIGHT MANUALLY

Just don't use the timer. Skip that step and change screenState to #$01 another way -- via a tile, a monster AI, or whatever. You can also skip the palette-changing part and use the night function a different way, gaining two extra monster configurations for every screen. Then change them as events in your story shape the world!
 
Last edited:

JamesNES

Well-known member
Nice one, I was poking around and saw all that data being compiled recently and was wondering how much work it'd take to get something going. You could even trigger a textbox when the night comes...!
 

ronin1011

Member
Hey are you referring to the GetScreenTriggerInfo located at line 163 or line 204 as the place to replace the code?
 

Bucket Mouse

Active member
Hey are you referring to the GetScreenTriggerInfo located at line 163 or line 204 as the place to replace the code?
Not sure what you're looking at. There's only one GetScreenTriggerInfo, and for me, it's down at line 291. Be sure you have the right script...
 

ronin1011

Member
I thought I did. \NesMaker\NESmaker_4_5_9\NESmaker_4_5_x\GameEngineData\Routines\BASE_4_5\Game\Subroutines -> doLoadScreenData.asm

1624647119021.png
 

ronin1011

Member
Not sure what you're looking at. There's only one GetScreenTriggerInfo, and for me, it's down at line 291. Be sure you have the right script...
This is the first instance of GetScreenTriggerInfo in my version of the script

1624647665759.png

Here is the code in my script:
 

Attachments

  • doLoadScreenData.txt
    11.9 KB · Views: 3

Bucket Mouse

Active member
That first one has clearly been edited out. When you see something green like that with a semicolon before it, that means it's not technically part of the script anymore.
 

Jonny

Well-known member
@ronin1011 Not sure if you already know but you can click "more options... / Code" to paste code. Might be easier than screen shots.
 

capacitor

New member
I tested in a scroling plataform game, and works!but only uncommenting the two instances of "LoadBackgroundPalettes", it's the only way to make work in the adventure module too for me, I don't no what I'm doing wrong, the fade transitions do work whitout the uncoments of the instantaneously mode?
 

Kanbei85

Member
Bucketmouse,

Thanks so much for doing this! Maybe soon we'll have all the broken features of NESmaker ironed out!

But I do have a question. You say that the variable screenState holds the trigger and day/night info. If so, why does the MACRO "TriggerScreen" not manipulate this variable in any way? Here is my TriggerScreen macro:

MACRO TriggerScreen arg0
;; arg0 = screen to change, usually held in variable screenType
TXA
STA tempx
TYA
STA tempy

lda arg0 ;; this is the value of the screen to change.
AND #%00000111 ;; look at last bits to know what bit to check, 0-7
TAX
LDA ValToBitTable_inverse,x
STA temp2
lda arg0 ;; this is the value of the screen to change

LSR
LSR
LSR
;;; now we have the right *byte* out of the 32 needed for 256 screen bytes
TAY
LDA screenTriggers,y ;; now the rigth bit is loaded into the accum
ORA temp2

STA screenTriggers,y

LDX tempx
LDY tempy
ENDM

I am interested in making macros to flip from Day to Night and vice versa, and then I need to make the needed changes to the TriggerScreen macro so that it will work to trigger the screen into the triggered state regardless of whether it is in the Day or Night state.

I have tried it, and simply changing the variable screenState to #$01 in a line of code does NOT result in changing the screen to Night mode.

Your thoughts?
 
Last edited:

Bucket Mouse

Active member
I don't exactly understand how the TriggerScreen macro is written. What it does is change screenState from #$00 to #$02, but at no visible point does that seem to happen. It must be somewhere in all that weird math stuff I'm bad at.
 

Kanbei85

Member
I don't exactly understand how the TriggerScreen macro is written. What it does is change screenState from #$00 to #$02, but at no visible point does that seem to happen. It must be somewhere in all that weird math stuff I'm bad at.

Okay, but as it stands, after following your instructions, simply writing into the code:

LDA #$01
STA screenState

... does NOT work to change the screen type into Night mode. We need some way to write in a trigger macro that will send the screen to Night mode, etc.

It would seem that the key to achieving this would be to understand how the TriggerScreen macro works, unless you know of some other way?

Have you ever tested actually trying to manually command the night as you said?
 
Last edited:

Bucket Mouse

Active member
Okay, looking back, here's what I really did: I flipped on the nightbyte flag, not the screenState flag, because screenState is taken care of in the other script. For example this one's for a tile:
Code:
CMP #$B2 ; entrance to dark cave
BNE +
LDA #$01
STA nightbyte
JMP warphere
+

There is one instance of screenState being changed, but nightbyte is changed at the same time.
Code:
LDA #$01
STA nightbyte
STA screenState
 

Kanbei85

Member
Okay, looking back, here's what I really did: I flipped on the nightbyte flag, not the screenState flag, because screenState is taken care of in the other script. For example this one's for a tile:
Code:
CMP #$B2 ; entrance to dark cave
BNE +
LDA #$01
STA nightbyte
JMP warphere
+

There is one instance of screenState being changed, but nightbyte is changed at the same time.
Code:
LDA #$01
STA nightbyte
STA screenState

Is nightbyte binary 0 or 1? or does it always take the same value as screenState?
 
Top Bottom